diff --git a/backend/app/api/v1/setting.go b/backend/app/api/v1/setting.go
index 5c09a500f..3d1714de1 100644
--- a/backend/app/api/v1/setting.go
+++ b/backend/app/api/v1/setting.go
@@ -119,6 +119,48 @@ func (b *BaseApi) UpdatePassword(c *gin.Context) {
 	helper.SuccessWithData(c, nil)
 }
 
+// @Tags System Setting
+// @Summary Update system ssl
+// @Description 修改系统 ssl 登录
+// @Accept json
+// @Param request body dto.SSLUpdate true "request"
+// @Success 200
+// @Security ApiKeyAuth
+// @Router /settings/ssl/update [post]
+// @x-panel-log {"bodyKeys":[ssl],"paramKeys":[],"BeforeFuntions":[],"formatZH":"修改系统 ssl => [ssl]","formatEN":"update system ssl => [ssl]"}
+func (b *BaseApi) UpdateSSL(c *gin.Context) {
+	var req dto.SSLUpdate
+	if err := c.ShouldBindJSON(&req); err != nil {
+		helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
+		return
+	}
+	if err := global.VALID.Struct(req); err != nil {
+		helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
+		return
+	}
+
+	if err := settingService.UpdateSSL(c, req); err != nil {
+		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
+		return
+	}
+	helper.SuccessWithData(c, nil)
+}
+
+// @Tags System Setting
+// @Summary Load system cert info
+// @Description 获取证书信息
+// @Success 200 {object} dto.SettingInfo
+// @Security ApiKeyAuth
+// @Router /settings/ssl/info [get]
+func (b *BaseApi) LoadFromCert(c *gin.Context) {
+	info, err := settingService.LoadFromCert()
+	if err != nil {
+		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
+		return
+	}
+	helper.SuccessWithData(c, info)
+}
+
 // @Tags System Setting
 // @Summary Update system port
 // @Description 更新系统端口
diff --git a/backend/app/dto/setting.go b/backend/app/dto/setting.go
index e4ad1fa4d..c3483bf78 100644
--- a/backend/app/dto/setting.go
+++ b/backend/app/dto/setting.go
@@ -17,6 +17,8 @@ type SettingInfo struct {
 
 	ServerPort             string `json:"serverPort"`
 	SecurityEntranceStatus string `json:"securityEntranceStatus"`
+	SSL                    string `json:"ssl"`
+	SSLType                string `json:"sslType"`
 	SecurityEntrance       string `json:"securityEntrance"`
 	ExpirationDays         string `json:"expirationDays"`
 	ExpirationTime         string `json:"expirationTime"`
@@ -40,6 +42,23 @@ type SettingUpdate struct {
 	Value string `json:"value"`
 }
 
+type SSLUpdate struct {
+	SSLType string `json:"sslType"`
+	Domain  string `json:"domain"`
+	SSL     string `json:"ssl" validate:"required,oneof=enable disable"`
+	Cert    string `json:"cert"`
+	Key     string `json:"key"`
+	SSLID   uint   `json:"sslID"`
+}
+type SSLInfo struct {
+	Domain   string `json:"domain"`
+	Timeout  string `json:"timeout"`
+	RootPath string `json:"rootPath"`
+	Cert     string `json:"cert"`
+	Key      string `json:"key"`
+	SSLID    uint   `json:"sslID"`
+}
+
 type PasswordUpdate struct {
 	OldPassword string `json:"oldPassword" validate:"required"`
 	NewPassword string `json:"newPassword" validate:"required"`
diff --git a/backend/app/service/setting.go b/backend/app/service/setting.go
index e645787ec..ee1a4cb9f 100644
--- a/backend/app/service/setting.go
+++ b/backend/app/service/setting.go
@@ -1,8 +1,14 @@
 package service
 
 import (
+	"crypto/tls"
+	"crypto/x509"
 	"encoding/json"
+	"encoding/pem"
+	"fmt"
+	"os"
 	"strconv"
+	"strings"
 	"time"
 
 	"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -12,7 +18,9 @@ import (
 	"github.com/1Panel-dev/1Panel/backend/utils/cmd"
 	"github.com/1Panel-dev/1Panel/backend/utils/common"
 	"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
+	"github.com/1Panel-dev/1Panel/backend/utils/ssl"
 	"github.com/gin-gonic/gin"
+	"github.com/pkg/errors"
 )
 
 type SettingService struct{}
@@ -23,6 +31,8 @@ type ISettingService interface {
 	UpdateEntrance(value string) error
 	UpdatePassword(c *gin.Context, old, new string) error
 	UpdatePort(port uint) error
+	UpdateSSL(c *gin.Context, req dto.SSLUpdate) error
+	LoadFromCert() (*dto.SSLInfo, error)
 	HandlePasswordExpired(c *gin.Context, old, new string) error
 }
 
@@ -111,6 +121,126 @@ func (u *SettingService) UpdatePort(port uint) error {
 	return nil
 }
 
+func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
+	if req.SSL == "disable" {
+		if err := settingRepo.Update("SSL", "disable"); err != nil {
+			return err
+		}
+		if err := settingRepo.Update("SSLType", "self"); err != nil {
+			return err
+		}
+		_ = os.Remove(global.CONF.System.BaseDir + "/1panel/secret/server.crt")
+		_ = os.Remove(global.CONF.System.BaseDir + "/1panel/secret/server.key")
+		go func() {
+			_, err := cmd.Exec("systemctl restart 1panel.service")
+			if err != nil {
+				global.LOG.Errorf("restart system failed, err: %v", err)
+			}
+		}()
+		return nil
+	}
+
+	if _, err := os.Stat(global.CONF.System.BaseDir + "/1panel/secret"); err != nil && os.IsNotExist(err) {
+		if err = os.MkdirAll(global.CONF.System.BaseDir+"/1panel/secret", os.ModePerm); err != nil {
+			return err
+		}
+	}
+	if err := settingRepo.Update("SSLType", req.SSLType); err != nil {
+		return err
+	}
+	if req.SSLType == "self" {
+		if len(req.Domain) == 0 {
+			return fmt.Errorf("load domain failed")
+		}
+		if err := ssl.GenerateSSL(req.Domain); err != nil {
+			return err
+		}
+	}
+	if req.SSLType == "select" {
+		sslInfo, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
+		if err != nil {
+			return err
+		}
+		req.Cert = sslInfo.Pem
+		req.Key = sslInfo.PrivateKey
+		req.SSLType = "import"
+		if err := settingRepo.Update("SSLID", strconv.Itoa(int(req.SSLID))); err != nil {
+			return err
+		}
+	}
+	if req.SSLType == "import" {
+		cert, err := os.OpenFile("/opt/1panel/secret/server.crt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
+		if err != nil {
+			return err
+		}
+		defer cert.Close()
+		if _, err := cert.WriteString(req.Cert); err != nil {
+			return err
+		}
+		key, err := os.OpenFile("/opt/1panel/secret/server.key", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
+		if err != nil {
+			return err
+		}
+		if _, err := key.WriteString(req.Key); err != nil {
+			return err
+		}
+		defer key.Close()
+	}
+	if err := checkCertValid(req.Domain); err != nil {
+		return err
+	}
+	if err := settingRepo.Update("SSL", req.SSL); err != nil {
+		return err
+	}
+	go func() {
+		_, err := cmd.Exec("systemctl restart 1panel.service")
+		if err != nil {
+			global.LOG.Errorf("restart system failed, err: %v", err)
+		}
+	}()
+	return nil
+}
+
+func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
+	ssl, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
+	if err != nil {
+		return nil, err
+	}
+	if ssl.Value == "disable" {
+		return &dto.SSLInfo{}, nil
+	}
+	sslType, err := settingRepo.Get(settingRepo.WithByKey("SSLType"))
+	if err != nil {
+		return nil, err
+	}
+	data, err := loadInfoFromCert()
+	if err != nil {
+		return nil, err
+	}
+	switch sslType.Value {
+	case "import":
+		if _, err := os.Stat(global.CONF.System.BaseDir + "/1panel/secret/server.crt"); err != nil {
+			return nil, fmt.Errorf("load server.crt file failed, err: %v", err)
+		}
+		certFile, _ := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.crt")
+		data.Cert = string(certFile)
+
+		if _, err := os.Stat(global.CONF.System.BaseDir + "/1panel/secret/server.key"); err != nil {
+			return nil, fmt.Errorf("load server.key file failed, err: %v", err)
+		}
+		keyFile, _ := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.key")
+		data.Key = string(keyFile)
+	case "select":
+		sslID, err := settingRepo.Get(settingRepo.WithByKey("SSLID"))
+		if err != nil {
+			return nil, err
+		}
+		id, _ := strconv.Atoi(sslID.Value)
+		data.SSLID = uint(id)
+	}
+	return data, nil
+}
+
 func (u *SettingService) HandlePasswordExpired(c *gin.Context, old, new string) error {
 	setting, err := settingRepo.Get(settingRepo.WithByKey("Password"))
 	if err != nil {
@@ -149,3 +279,71 @@ func (u *SettingService) UpdatePassword(c *gin.Context, old, new string) error {
 	_ = global.SESSION.Clean()
 	return nil
 }
+
+func loadInfoFromCert() (*dto.SSLInfo, error) {
+	var info dto.SSLInfo
+	certFile := global.CONF.System.BaseDir + "/1panel/secret/server.crt"
+	if _, err := os.Stat(certFile); err != nil {
+		return &info, err
+	}
+	certData, err := os.ReadFile(certFile)
+	if err != nil {
+		return &info, err
+	}
+	certBlock, _ := pem.Decode(certData)
+	if certBlock == nil {
+		return &info, err
+	}
+	certObj, err := x509.ParseCertificate(certBlock.Bytes)
+	if err != nil {
+		return &info, err
+	}
+	var domains []string
+	if len(certObj.IPAddresses) != 0 {
+		for _, ip := range certObj.IPAddresses {
+			domains = append(domains, ip.String())
+		}
+	}
+	if len(certObj.DNSNames) != 0 {
+		domains = append(domains, certObj.DNSNames...)
+	}
+	return &dto.SSLInfo{
+		Domain:   strings.Join(domains, ","),
+		Timeout:  certObj.NotAfter.Format("2006-01-02 15:04:05"),
+		RootPath: global.CONF.System.BaseDir + "/1panel/secret/server.crt",
+	}, nil
+}
+
+func checkCertValid(domain string) error {
+	certificate, err := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.crt")
+	if err != nil {
+		return err
+	}
+	key, err := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.key")
+	if err != nil {
+		return err
+	}
+	if _, err = tls.X509KeyPair(certificate, key); err != nil {
+		return err
+	}
+	certObj, err := x509.ParseCertificate(certificate)
+	if err != nil {
+		return err
+	}
+
+	if len(certObj.IPAddresses) != 0 {
+		for _, ip := range certObj.IPAddresses {
+			if ip.String() == domain {
+				return nil
+			}
+		}
+	}
+	if len(certObj.DNSNames) != 0 {
+		for _, ip := range certObj.DNSNames {
+			if ip == domain {
+				return nil
+			}
+		}
+	}
+	return errors.New("The domain name or ip address does not match")
+}
diff --git a/backend/configs/system.go b/backend/configs/system.go
index 2aa26bc7b..02c73ef97 100644
--- a/backend/configs/system.go
+++ b/backend/configs/system.go
@@ -2,6 +2,7 @@ package configs
 
 type System struct {
 	Port       string `mapstructure:"port"`
+	SSL        string `mapstructure:"ssl"`
 	DbFile     string `mapstructure:"db_file"`
 	DbPath     string `mapstructure:"db_path"`
 	LogPath    string `mapstructure:"log_path"`
diff --git a/backend/init/hook/hook.go b/backend/init/hook/hook.go
index c5affaa61..a2fdc989d 100644
--- a/backend/init/hook/hook.go
+++ b/backend/init/hook/hook.go
@@ -17,6 +17,11 @@ func Init() {
 		global.LOG.Errorf("load service encrypt key from setting failed, err: %v", err)
 	}
 	global.CONF.System.EncryptKey = enptrySetting.Value
+	sslSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
+	if err != nil {
+		global.LOG.Errorf("load service ssl from setting failed, err: %v", err)
+	}
+	global.CONF.System.SSL = sslSetting.Value
 
 	if _, err := settingRepo.Get(settingRepo.WithByKey("SystemStatus")); err != nil {
 		_ = settingRepo.Create("SystemStatus", "Free")
diff --git a/backend/init/migration/migrate.go b/backend/init/migration/migrate.go
index b1d79d7b4..992795602 100644
--- a/backend/init/migration/migrate.go
+++ b/backend/init/migration/migrate.go
@@ -25,7 +25,7 @@ func Init() {
 		migrations.UpdateTableApp,
 		migrations.UpdateTableHost,
 		migrations.UpdateTableWebsite,
-		migrations.AddEntranceStatus,
+		migrations.AddEntranceAndSSL,
 	})
 	if err := m.Migrate(); err != nil {
 		global.LOG.Error(err)
diff --git a/backend/init/migration/migrations/init.go b/backend/init/migration/migrations/init.go
index f6dc843b5..f16517a26 100644
--- a/backend/init/migration/migrations/init.go
+++ b/backend/init/migration/migrations/init.go
@@ -288,8 +288,8 @@ var UpdateTableWebsite = &gormigrate.Migration{
 	},
 }
 
-var AddEntranceStatus = &gormigrate.Migration{
-	ID: "20230414-add-entrance-status",
+var AddEntranceAndSSL = &gormigrate.Migration{
+	ID: "20230414-add-entrance-and-ssl",
 	Migrate: func(tx *gorm.DB) error {
 		if err := tx.Create(&model.Setting{Key: "SecurityEntranceStatus", Value: "disable"}).Error; err != nil {
 			return err
@@ -297,6 +297,15 @@ var AddEntranceStatus = &gormigrate.Migration{
 		if err := tx.Model(&model.Setting{}).Where("key = ?", "SecurityEntrance").Updates(map[string]interface{}{"value": ""}).Error; err != nil {
 			return err
 		}
+		if err := tx.Create(&model.Setting{Key: "SSLType", Value: "self"}).Error; err != nil {
+			return err
+		}
+		if err := tx.Create(&model.Setting{Key: "SSLID", Value: "0"}).Error; err != nil {
+			return err
+		}
+		if err := tx.Create(&model.Setting{Key: "SSL", Value: "disable"}).Error; err != nil {
+			return err
+		}
 		return tx.AutoMigrate(&model.Website{})
 	},
 }
diff --git a/backend/router/ro_setting.go b/backend/router/ro_setting.go
index cb2220c87..2ca54f689 100644
--- a/backend/router/ro_setting.go
+++ b/backend/router/ro_setting.go
@@ -24,6 +24,8 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
 		settingRouter.POST("/update", baseApi.UpdateSetting)
 		settingRouter.POST("/entrance/enable", baseApi.UpdateEntrance)
 		settingRouter.POST("/port/update", baseApi.UpdatePort)
+		settingRouter.POST("/ssl/update", baseApi.UpdateSSL)
+		settingRouter.GET("/ssl/info", baseApi.LoadFromCert)
 		settingRouter.POST("/password/update", baseApi.UpdatePassword)
 		settingRouter.POST("/time/sync", baseApi.SyncTime)
 		settingRouter.POST("/monitor/clean", baseApi.CleanMonitor)
diff --git a/backend/server/server.go b/backend/server/server.go
index c46beebcd..032a06a3b 100644
--- a/backend/server/server.go
+++ b/backend/server/server.go
@@ -1,8 +1,11 @@
 package server
 
 import (
+	"crypto/tls"
 	"encoding/gob"
 	"fmt"
+	"net/http"
+	"os"
 	"time"
 
 	"github.com/1Panel-dev/1Panel/backend/init/app"
@@ -43,22 +46,42 @@ func Start() {
 
 	rootRouter := router.Routers()
 	address := fmt.Sprintf(":%s", global.CONF.System.Port)
-	s := initServer(address, rootRouter)
-	global.LOG.Infof("server run success on %s", global.CONF.System.Port)
-	if err := s.ListenAndServe(); err != nil {
-		global.LOG.Error(err)
-		panic(err)
-	}
-}
-
-type server interface {
-	ListenAndServe() error
-}
-
-func initServer(address string, router *gin.Engine) server {
-	s := endless.NewServer(address, router)
+	s := endless.NewServer(address, rootRouter)
 	s.ReadHeaderTimeout = 20 * time.Second
 	s.WriteTimeout = 60 * time.Second
 	s.MaxHeaderBytes = 1 << 20
-	return s
+
+	if global.CONF.System.SSL == "disable" {
+		global.LOG.Infof("server run success on %s with http", global.CONF.System.Port)
+		if err := s.ListenAndServe(); err != nil {
+			global.LOG.Error(err)
+			panic(err)
+		}
+	} else {
+		certificate, err := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.crt")
+		if err != nil {
+			panic(err)
+		}
+		key, err := os.ReadFile(global.CONF.System.BaseDir + "/1panel/secret/server.key")
+		if err != nil {
+			panic(err)
+		}
+		cert, err := tls.X509KeyPair(certificate, key)
+		if err != nil {
+			panic(err)
+		}
+		s := &http.Server{
+			Addr:    address,
+			Handler: rootRouter,
+			TLSConfig: &tls.Config{
+				Certificates: []tls.Certificate{cert},
+			},
+		}
+
+		global.LOG.Infof("server run success on %s with https", global.CONF.System.Port)
+		if err := s.ListenAndServeTLS("", ""); err != nil {
+			global.LOG.Error(err)
+			panic(err)
+		}
+	}
 }
diff --git a/backend/utils/ssl/ssl.go b/backend/utils/ssl/ssl.go
new file mode 100644
index 000000000..34f59792d
--- /dev/null
+++ b/backend/utils/ssl/ssl.go
@@ -0,0 +1,112 @@
+package ssl
+
+import (
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/pem"
+	"math/big"
+	"net"
+	"os"
+	"time"
+
+	"github.com/1Panel-dev/1Panel/backend/global"
+)
+
+func GenerateSSL(domain string) error {
+	rootPrivateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
+	ipItem := net.ParseIP(domain)
+	isIP := false
+	if len(ipItem) != 0 {
+		isIP = true
+	}
+
+	rootTemplate := x509.Certificate{
+		SerialNumber:          big.NewInt(1),
+		Subject:               pkix.Name{CommonName: "1Panel Root CA"},
+		NotBefore:             time.Now(),
+		NotAfter:              time.Now().AddDate(10, 0, 0),
+		BasicConstraintsValid: true,
+		IsCA:                  true,
+		KeyUsage:              x509.KeyUsageCertSign,
+	}
+	if isIP {
+		rootTemplate.IPAddresses = []net.IP{ipItem}
+	} else {
+		rootTemplate.DNSNames = []string{domain}
+	}
+
+	rootCertBytes, _ := x509.CreateCertificate(rand.Reader, &rootTemplate, &rootTemplate, &rootPrivateKey.PublicKey, rootPrivateKey)
+	rootCertBlock := &pem.Block{
+		Type:  "CERTIFICATE",
+		Bytes: rootCertBytes,
+	}
+
+	interPrivateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
+	interTemplate := x509.Certificate{
+		SerialNumber:          big.NewInt(2),
+		Subject:               pkix.Name{CommonName: "1Panel Intermediate CA"},
+		NotBefore:             time.Now(),
+		NotAfter:              time.Now().AddDate(10, 0, 0),
+		BasicConstraintsValid: true,
+		IsCA:                  true,
+		KeyUsage:              x509.KeyUsageCertSign,
+	}
+	if isIP {
+		interTemplate.IPAddresses = []net.IP{ipItem}
+	} else {
+		interTemplate.DNSNames = []string{domain}
+	}
+
+	interCertBytes, _ := x509.CreateCertificate(rand.Reader, &interTemplate, &rootTemplate, &interPrivateKey.PublicKey, rootPrivateKey)
+	interCertBlock := &pem.Block{
+		Type:  "CERTIFICATE",
+		Bytes: interCertBytes,
+	}
+
+	clientPrivateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
+	clientTemplate := x509.Certificate{
+		SerialNumber: big.NewInt(3),
+		Subject:      pkix.Name{CommonName: domain},
+		NotBefore:    time.Now(),
+		NotAfter:     time.Now().AddDate(10, 0, 0),
+		KeyUsage:     x509.KeyUsageDigitalSignature,
+		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
+	}
+	if isIP {
+		clientTemplate.IPAddresses = []net.IP{ipItem}
+	} else {
+		clientTemplate.DNSNames = []string{domain}
+	}
+
+	clientCertBytes, _ := x509.CreateCertificate(rand.Reader, &clientTemplate, &interTemplate, &clientPrivateKey.PublicKey, interPrivateKey)
+	clientCertBlock := &pem.Block{
+		Type:  "CERTIFICATE",
+		Bytes: clientCertBytes,
+	}
+
+	pemBytes := []byte{}
+	pemBytes = append(pemBytes, pem.EncodeToMemory(clientCertBlock)...)
+	pemBytes = append(pemBytes, pem.EncodeToMemory(interCertBlock)...)
+	pemBytes = append(pemBytes, pem.EncodeToMemory(rootCertBlock)...)
+	certOut, err := os.OpenFile(global.CONF.System.BaseDir+"/1panel/secret/server.crt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
+	if err != nil {
+		return err
+	}
+	defer certOut.Close()
+	if _, err := certOut.Write(pemBytes); err != nil {
+		return err
+	}
+
+	keyOut, err := os.OpenFile(global.CONF.System.BaseDir+"/1panel/secret/server.key", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
+	if err != nil {
+		return err
+	}
+	defer keyOut.Close()
+	if err := pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(clientPrivateKey)}); err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts
index f2cececf5..a6a96a64c 100644
--- a/frontend/src/api/index.ts
+++ b/frontend/src/api/index.ts
@@ -26,7 +26,6 @@ class RequestHttp {
                         ...config.headers,
                     };
                 }
-
                 return {
                     ...config,
                 };
diff --git a/frontend/src/api/interface/setting.ts b/frontend/src/api/interface/setting.ts
index 51e7f2f14..d243657fe 100644
--- a/frontend/src/api/interface/setting.ts
+++ b/frontend/src/api/interface/setting.ts
@@ -16,6 +16,8 @@ export namespace Setting {
 
         serverPort: number;
         securityEntranceStatus: string;
+        ssl: string;
+        sslType: string;
         securityEntrance: string;
         expirationDays: number;
         expirationTime: string;
@@ -35,6 +37,22 @@ export namespace Setting {
         key: string;
         value: string;
     }
+    export interface SSLUpdate {
+        ssl: string;
+        domain: string;
+        sslType: string;
+        cert: string;
+        key: string;
+        sslID: number;
+    }
+    export interface SSLInfo {
+        domain: string;
+        timeout: string;
+        rootPath: string;
+        cert: string;
+        key: string;
+        sslID: number;
+    }
     export interface PasswordUpdate {
         oldPassword: string;
         newPassword: string;
diff --git a/frontend/src/api/modules/setting.ts b/frontend/src/api/modules/setting.ts
index d075cf659..76e7c8df4 100644
--- a/frontend/src/api/modules/setting.ts
+++ b/frontend/src/api/modules/setting.ts
@@ -28,6 +28,13 @@ export const updatePort = (param: Setting.PortUpdate) => {
     return http.post(`/settings/port/update`, param);
 };
 
+export const updateSSL = (param: Setting.SSLUpdate) => {
+    return http.post(`/settings/ssl/update`, param);
+};
+export const loadSSLInfo = () => {
+    return http.get<Setting.SSLInfo>(`/settings/ssl/info`);
+};
+
 export const handleExpired = (param: Setting.PasswordUpdate) => {
     return http.post(`/settings/expired/handle`, param);
 };
diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts
index c0ee585e4..0b9958449 100644
--- a/frontend/src/lang/modules/en.ts
+++ b/frontend/src/lang/modules/en.ts
@@ -872,6 +872,18 @@ const message = {
         mfaHelper2: 'Scan the following QR code using the mobile app to obtain the 6-digit verification code',
         mfaHelper3: 'Enter six digits from the app',
 
+        https: 'Setting up HTTPS protocol access for the panel can enhance the security of panel access.',
+        selfSigned: 'Self signed',
+        selfSignedHelper:
+            'It is normal for self-signed certificates to be not trusted by browsers and display a security warning as the certificate is not issued by a trusted third party.',
+        import: 'Import',
+        select: 'Select',
+        domainOrIP: 'Domain/IP:',
+        timeOut: 'Timeout',
+        rootCrtDownload: 'Root certificate download',
+        primaryKey: 'Primary key',
+        certificate: 'Certificate',
+
         snapshot: 'Snapshot',
         thirdPartySupport: 'Only third-party accounts are supported',
         recoverDetail: 'Recover detail',
diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts
index 5e1812bba..bc1c4a755 100644
--- a/frontend/src/lang/modules/zh.ts
+++ b/frontend/src/lang/modules/zh.ts
@@ -857,6 +857,17 @@ const message = {
         password: '密码',
         path: '路径',
 
+        https: '为面板设置 https 协议访问,提升面板访问安全性',
+        selfSigned: '自签名',
+        selfSignedHelper: '自签证书,不被浏览器信任,显示不安全是正常现象',
+        import: '导入',
+        select: '选择已有',
+        domainOrIP: '域名/IP:',
+        timeOut: '过期时间:',
+        rootCrtDownload: '根证书下载',
+        primaryKey: '密钥',
+        certificate: '证书',
+
         snapshot: '快照',
         thirdPartySupport: '仅支持第三方账号',
         recoverDetail: '恢复详情',
@@ -909,6 +920,8 @@ const message = {
         mfaHelper1: '下载两步验证手机应用 如:',
         mfaHelper2: '使用手机应用扫描以下二维码,获取 6 位验证码',
         mfaHelper3: '输入手机应用上的 6 位数字',
+        sslDisable: '禁用',
+        sslDisableHelper: '禁用 https 服务,需要重启面板才能生效,是否继续!',
 
         monitor: '监控',
         enableMonitor: '监控状态',
diff --git a/frontend/src/views/setting/safe/index.vue b/frontend/src/views/setting/safe/index.vue
index 2c44da8a3..5b325b740 100644
--- a/frontend/src/views/setting/safe/index.vue
+++ b/frontend/src/views/setting/safe/index.vue
@@ -5,7 +5,7 @@
                 <el-form :model="form" ref="panelFormRef" v-loading="loading" label-position="left" label-width="180px">
                     <el-row>
                         <el-col :span="1"><br /></el-col>
-                        <el-col :span="10">
+                        <el-col :span="16">
                             <el-form-item :label="$t('setting.panelPort')" :rules="Rules.port" prop="serverPort">
                                 <el-input clearable v-model.number="form.serverPort">
                                     <template #append>
@@ -133,6 +133,22 @@
                                     </ul>
                                 </el-card>
                             </el-form-item>
+
+                            <el-form-item label="https" required prop="ssl">
+                                <el-switch
+                                    @change="handleSSL"
+                                    v-model="form.ssl"
+                                    active-value="enable"
+                                    inactive-value="disable"
+                                />
+                                <span class="input-help">{{ $t('setting.https') }}</span>
+                                <SSLSetting
+                                    :type="form.sslType"
+                                    :status="form.ssl"
+                                    v-if="sslShow"
+                                    style="width: 100%"
+                                />
+                            </el-form-item>
                         </el-col>
                     </el-row>
                 </el-form>
@@ -165,6 +181,7 @@ import { ref, reactive, onMounted } from 'vue';
 import { ElForm, ElMessageBox } from 'element-plus';
 import { Setting } from '@/api/interface/setting';
 import LayoutContent from '@/layout/layout-content.vue';
+import SSLSetting from '@/views/setting/safe/ssl/index.vue';
 import DrawerHeader from '@/components/drawer-header/index.vue';
 import {
     updateSetting,
@@ -174,6 +191,7 @@ import {
     updatePort,
     getSystemAvailable,
     updateEntrance,
+    updateSSL,
 } from '@/api/modules/setting';
 import i18n from '@/lang';
 import { Rules, checkNumberRange } from '@/global/form-rules';
@@ -183,9 +201,12 @@ import { GlobalStore } from '@/store';
 const globalStore = GlobalStore();
 
 const loading = ref(false);
+
 const form = reactive({
     serverPort: 9999,
     securityEntranceStatus: 'disable',
+    ssl: 'disable',
+    sslType: 'self',
     securityEntrance: '',
     expirationDays: 0,
     expirationTime: '',
@@ -200,11 +221,20 @@ const timeoutForm = reactive({
     days: 0,
 });
 
+const sslShow = ref();
+const oldSSLStatus = ref();
+
 const search = async () => {
     const res = await getSettingInfo();
     form.serverPort = Number(res.data.serverPort);
     form.securityEntranceStatus = res.data.securityEntranceStatus;
     isEntranceShow.value = res.data.securityEntranceStatus === 'enable';
+    form.ssl = res.data.ssl;
+    oldSSLStatus.value = res.data.ssl;
+    form.sslType = res.data.sslType;
+    if (form.ssl === 'enable') {
+        sslShow.value = true;
+    }
     form.securityEntrance = res.data.securityEntrance;
     form.expirationDays = Number(res.data.expirationDays);
     form.expirationTime = res.data.expirationTime;
@@ -323,6 +353,33 @@ const handleEntrance = async () => {
     }
 };
 
+const handleSSL = async () => {
+    if (form.ssl === 'enable') {
+        sslShow.value = true;
+        return;
+    }
+    if (form.ssl === oldSSLStatus.value) {
+        sslShow.value = false;
+        return;
+    }
+    ElMessageBox.confirm(i18n.global.t('setting.sslDisableHelper'), i18n.global.t('setting.sslDisable'), {
+        confirmButtonText: i18n.global.t('commons.button.confirm'),
+        cancelButtonText: i18n.global.t('commons.button.cancel'),
+        type: 'info',
+    })
+        .then(async () => {
+            sslShow.value = false;
+            await updateSSL({ ssl: 'disable', domain: '', sslType: '', key: '', cert: '', sslID: 0 });
+            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
+            let href = window.location.href;
+            let address = href.split('://')[1];
+            window.open(`http://${address}/`, '_self');
+        })
+        .catch(() => {
+            form.ssl = 'enable';
+        });
+};
+
 const onSaveEntrance = async () => {
     const reg = /^[A-Za-z0-9]{6,10}$/;
     if ((!reg.test(form.securityEntrance) && form.securityEntrance !== '') || form.securityEntrance === '') {
diff --git a/frontend/src/views/setting/safe/ssl/index.vue b/frontend/src/views/setting/safe/ssl/index.vue
new file mode 100644
index 000000000..f7d9d8d0b
--- /dev/null
+++ b/frontend/src/views/setting/safe/ssl/index.vue
@@ -0,0 +1,194 @@
+<template>
+    <div>
+        <el-card>
+            <el-form ref="formRef" label-position="top" :model="form" :rules="rules">
+                <el-radio-group v-model="sslItemType">
+                    <el-radio label="self">{{ $t('setting.selfSigned') }}</el-radio>
+                    <el-radio label="select">{{ $t('setting.select') }}</el-radio>
+                    <el-radio label="import">{{ $t('setting.import') }}</el-radio>
+                </el-radio-group>
+                <span class="input-help" v-if="sslItemType === 'self'">{{ $t('setting.selfSignedHelper') }}</span>
+                <div v-if="sslInfo.timeout">
+                    <el-tag>{{ $t('setting.domainOrIP') }} {{ sslInfo.domain }}</el-tag>
+                    <el-tag style="margin-left: 5px">{{ $t('setting.timeOut') }} {{ sslInfo.timeout }}</el-tag>
+                    <el-button
+                        @click="onDownload"
+                        style="margin-left: 5px"
+                        v-if="sslItemType === 'self'"
+                        type="primary"
+                        link
+                        icon="Download"
+                    >
+                        {{ $t('setting.rootCrtDownload') }}
+                    </el-button>
+                </div>
+
+                <div v-if="sslItemType === 'import'">
+                    <el-form-item :label="$t('setting.primaryKey')" prop="key">
+                        <el-input v-model="form.key" :autosize="{ minRows: 2, maxRows: 6 }" type="textarea" />
+                    </el-form-item>
+                    <el-form-item class="margintop" :label="$t('setting.certificate')" prop="cert">
+                        <el-input v-model="form.cert" :autosize="{ minRows: 2, maxRows: 6 }" type="textarea" />
+                    </el-form-item>
+                </div>
+
+                <div v-if="sslItemType === 'select'">
+                    <el-form-item :label="$t('setting.certificate')" prop="sslID">
+                        <el-select v-model="form.sslID" @change="changeSSl(form.sslID)">
+                            <el-option
+                                v-for="(item, index) in sslList"
+                                :key="index"
+                                :label="item.primaryDomain"
+                                :value="item.id"
+                            ></el-option>
+                        </el-select>
+                    </el-form-item>
+                    <el-descriptions
+                        class="margintop"
+                        :column="5"
+                        border
+                        direction="vertical"
+                        v-if="form.sslID > 0 && itemSSL"
+                    >
+                        <el-descriptions-item :label="$t('website.primaryDomain')">
+                            {{ itemSSL.primaryDomain }}
+                        </el-descriptions-item>
+                        <el-descriptions-item :label="$t('website.otherDomains')">
+                            {{ itemSSL.domains }}
+                        </el-descriptions-item>
+                        <el-descriptions-item :label="$t('ssl.provider')">
+                            {{ getProvider(itemSSL.provider) }}
+                        </el-descriptions-item>
+                        <el-descriptions-item
+                            :label="$t('ssl.acmeAccount')"
+                            v-if="itemSSL.acmeAccount?.email && itemSSL.provider !== 'manual'"
+                        >
+                            {{ itemSSL.acmeAccount.email }}
+                        </el-descriptions-item>
+                        <el-descriptions-item :label="$t('website.expireDate')">
+                            {{ dateFormatSimple(itemSSL.expireDate) }}
+                        </el-descriptions-item>
+                    </el-descriptions>
+                </div>
+                <el-button style="margin-top: 20px" type="primary" @click="onSaveSSL(formRef)">
+                    {{ $t('commons.button.saveAndEnable') }}
+                </el-button>
+            </el-form>
+        </el-card>
+    </div>
+</template>
+<script lang="ts" setup>
+import { Website } from '@/api/interface/Website';
+import { loadSSLInfo } from '@/api/modules/setting';
+import { dateFormatSimple, getProvider } from '@/utils/util';
+import { ListSSL } from '@/api/modules/website';
+import { nextTick, onMounted, reactive, ref } from 'vue';
+import i18n from '@/lang';
+import { MsgSuccess } from '@/utils/message';
+import { updateSSL } from '@/api/modules/setting';
+import { DownloadByPath } from '@/api/modules/files';
+import { Rules } from '@/global/form-rules';
+import { FormInstance } from 'element-plus';
+
+const form = reactive({
+    ssl: 'enable',
+    domain: '',
+    sslType: 'self',
+    sslID: null as number,
+    cert: '',
+    key: '',
+    rootPath: '',
+});
+
+const rules = reactive({
+    cert: [Rules.requiredInput],
+    key: [Rules.requiredInput],
+    sslID: [Rules.requiredSelect],
+});
+
+const formRef = ref<FormInstance>();
+
+const props = defineProps({
+    type: {
+        type: String,
+        default: 'self',
+    },
+});
+
+const sslInfo = reactive({
+    domain: '',
+    timeout: '',
+});
+const sslList = ref();
+const itemSSL = ref();
+const sslItemType = ref('self');
+
+const loadInfo = async () => {
+    await loadSSLInfo().then(async (res) => {
+        sslInfo.domain = res.data.domain || '';
+        sslInfo.timeout = res.data.timeout || '';
+        form.cert = res.data.cert;
+        form.key = res.data.key;
+        form.rootPath = res.data.rootPath;
+        if (res.data.sslID) {
+            form.sslID = res.data.sslID;
+            const ssls = await ListSSL({});
+            sslList.value = ssls.data || [];
+            changeSSl(form.sslID);
+        }
+    });
+};
+
+const loadSSLs = async () => {
+    const res = await ListSSL({});
+    sslList.value = res.data || [];
+};
+
+const changeSSl = (sslid: number) => {
+    const res = sslList.value.filter((element: Website.SSL) => {
+        return element.id == sslid;
+    });
+    itemSSL.value = res[0];
+};
+
+const onDownload = async () => {
+    const file = await DownloadByPath(form.rootPath);
+    const downloadUrl = window.URL.createObjectURL(new Blob([file]));
+    const a = document.createElement('a');
+    a.style.display = 'none';
+    a.href = downloadUrl;
+    a.download = 'server.crt';
+    const event = new MouseEvent('click');
+    a.dispatchEvent(event);
+};
+
+const onSaveSSL = async (formEl: FormInstance | undefined) => {
+    if (!formEl) return;
+    formEl.validate(async (valid) => {
+        if (!valid) return;
+        form.sslType = sslItemType.value;
+        let href = window.location.href;
+        form.domain = href.split('//')[1].split(':')[0];
+        await updateSSL(form).then(() => {
+            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
+            let href = window.location.href;
+            let address = href.split('://')[1];
+            window.open(`https://${address}/`, '_self');
+        });
+    });
+};
+
+onMounted(() => {
+    nextTick(() => {
+        sslItemType.value = props.type;
+        loadInfo();
+    });
+    loadSSLs();
+});
+</script>
+
+<style scoped lang="scss">
+.margintop {
+    margin-top: 10px;
+}
+</style>