diff --git a/Justfile b/Justfile
index 9ee7eb3..0b8f814 100644
--- a/Justfile
+++ b/Justfile
@@ -9,6 +9,8 @@ dev:
     export PAGES_DOMAIN=localhost.mock.directory
     export RAW_DOMAIN=raw.localhost.mock.directory
     export PORT=4430
+    export HTTP_PORT=8880
+    export ENABLE_HTTP_SERVER=true
     export LOG_LEVEL=trace
     go run -tags '{{TAGS}}' .
 
diff --git a/cmd/main.go b/cmd/main.go
index 8a65d43..aa00f54 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -48,7 +48,8 @@ func Serve(ctx *cli.Context) error {
 	mainDomainSuffix := ctx.String("pages-domain")
 	rawInfoPage := ctx.String("raw-info-page")
 	listeningHost := ctx.String("host")
-	listeningSSLAddress := fmt.Sprintf("%s:%d", listeningHost, ctx.Uint("port"))
+	listeningSSLPort := ctx.Uint("port")
+	listeningSSLAddress := fmt.Sprintf("%s:%d", listeningHost, listeningSSLPort)
 	listeningHTTPAddress := fmt.Sprintf("%s:%d", listeningHost, ctx.Uint("http-port"))
 	enableHTTPServer := ctx.Bool("enable-http-server")
 
@@ -93,7 +94,7 @@ func Serve(ctx *cli.Context) error {
 	}
 
 	// Create listener for SSL connections
-	log.Info().Msgf("Listening on https://%s", listeningSSLAddress)
+	log.Info().Msgf("Create TCP listener for SSL on %s", listeningSSLAddress)
 	listener, err := net.Listen("tcp", listeningSSLAddress)
 	if err != nil {
 		return fmt.Errorf("couldn't create listener: %v", err)
@@ -113,7 +114,7 @@ func Serve(ctx *cli.Context) error {
 
 	if enableHTTPServer {
 		// Create handler for http->https redirect and http acme challenges
-		httpHandler := certificates.SetupHTTPACMEChallengeServer(challengeCache)
+		httpHandler := certificates.SetupHTTPACMEChallengeServer(challengeCache, listeningSSLPort)
 
 		// Create listener for http and start listening
 		go func() {
@@ -133,7 +134,7 @@ func Serve(ctx *cli.Context) error {
 		dnsLookupCache, canonicalDomainCache)
 
 	// Start the ssl listener
-	log.Info().Msgf("Start listening on %s", listener.Addr())
+	log.Info().Msgf("Start SSL server using TCP listener on %s", listener.Addr())
 	if err := http.Serve(listener, sslHandler); err != nil {
 		log.Panic().Err(err).Msg("Couldn't start fastServer")
 	}
diff --git a/integration/get_test.go b/integration/get_test.go
index b70eeed..8c9a805 100644
--- a/integration/get_test.go
+++ b/integration/get_test.go
@@ -193,6 +193,18 @@ func TestGetOptions(t *testing.T) {
 	assert.EqualValues(t, "GET, HEAD, OPTIONS", resp.Header.Get("Allow"))
 }
 
+func TestHttpRedirect(t *testing.T) {
+	log.Println("=== TestHttpRedirect ===")
+	resp, err := http.Get("http://mock-pages.codeberg-test.org:8880/README.md")
+	assert.NoError(t, err)
+	if !assert.NotNil(t, resp) {
+		t.FailNow()
+	}
+	assert.EqualValues(t, http.StatusMovedPermanently, resp.StatusCode)
+	assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type"))
+	assert.EqualValues(t, "https://mock-pages.codeberg-test.org:4430/README.md", resp.Header.Get("Location"))
+}
+
 func getTestHTTPSClient() *http.Client {
 	cookieJar, _ := cookiejar.New(nil)
 	return &http.Client{
diff --git a/integration/main_test.go b/integration/main_test.go
index 3e0e187..a6579fd 100644
--- a/integration/main_test.go
+++ b/integration/main_test.go
@@ -40,6 +40,8 @@ func startServer(ctx context.Context) error {
 	setEnvIfNotSet("PAGES_DOMAIN", "localhost.mock.directory")
 	setEnvIfNotSet("RAW_DOMAIN", "raw.localhost.mock.directory")
 	setEnvIfNotSet("PORT", "4430")
+	setEnvIfNotSet("HTTP_PORT", "8880")
+	setEnvIfNotSet("ENABLE_HTTP_SERVER", "true")
 	setEnvIfNotSet("DB_TYPE", "sqlite3")
 
 	app := cli.NewApp()
diff --git a/server/certificates/cached_challengers.go b/server/certificates/cached_challengers.go
index 02474b3..6bd8ff7 100644
--- a/server/certificates/cached_challengers.go
+++ b/server/certificates/cached_challengers.go
@@ -1,15 +1,17 @@
 package certificates
 
 import (
+	"fmt"
 	"net/http"
+	"net/url"
 	"strings"
 	"time"
 
 	"github.com/go-acme/lego/v4/challenge"
+	"github.com/rs/zerolog/log"
 
 	"codeberg.org/codeberg/pages/server/cache"
 	"codeberg.org/codeberg/pages/server/context"
-	"codeberg.org/codeberg/pages/server/utils"
 )
 
 type AcmeTLSChallengeProvider struct {
@@ -44,17 +46,38 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error {
 	return nil
 }
 
-func SetupHTTPACMEChallengeServer(challengeCache cache.SetGetKey) http.HandlerFunc {
+func SetupHTTPACMEChallengeServer(challengeCache cache.SetGetKey, sslPort uint) http.HandlerFunc {
+	// not null if a custom port needs to be added to https redirects
+	portPart := ""
+	if sslPort != 443 {
+		portPart = fmt.Sprintf(":%d", sslPort)
+	}
+
 	return func(w http.ResponseWriter, req *http.Request) {
 		ctx := context.New(w, req)
+		domain := ctx.TrimHostPort()
+
+		// it's an acme request
 		if strings.HasPrefix(ctx.Path(), challengePath) {
-			challenge, ok := challengeCache.Get(utils.TrimHostPort(ctx.Host()) + "/" + strings.TrimPrefix(ctx.Path(), challengePath))
+			challenge, ok := challengeCache.Get(domain + "/" + strings.TrimPrefix(ctx.Path(), challengePath))
 			if !ok || challenge == nil {
+				log.Info().Msgf("HTTP-ACME challenge for '%s' failed: token not found", domain)
 				ctx.String("no challenge for this token", http.StatusNotFound)
 			}
+			log.Info().Msgf("HTTP-ACME challenge for '%s' succeeded", domain)
 			ctx.String(challenge.(string))
+
 		} else {
-			ctx.Redirect("https://"+ctx.Host()+ctx.Path(), http.StatusMovedPermanently)
+			// it's a normal http request that needs to be redirected
+			u, err := url.Parse(fmt.Sprintf("https://%s%s%s", domain, portPart, ctx.Path()))
+			if err != nil {
+				log.Error().Err(err).Msg("could not craft http to https redirect")
+				ctx.String("", http.StatusInternalServerError)
+			}
+
+			newURL := u.String()
+			log.Debug().Msgf("redirect http to https: %s", newURL)
+			ctx.Redirect(newURL, http.StatusMovedPermanently)
 		}
 	}
 }
diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go
index 707672c..ce1420d 100644
--- a/server/certificates/certificates.go
+++ b/server/certificates/certificates.go
@@ -47,6 +47,7 @@ func TLSConfig(mainDomainSuffix string,
 					if proto != tlsalpn01.ACMETLS1Protocol {
 						continue
 					}
+					log.Info().Msgf("Detect ACME-TLS1 challenge for '%s'", domain)
 
 					challenge, ok := challengeCache.Get(domain)
 					if !ok {