diff --git a/internal/router/session.go b/internal/router/session.go
index be29b01c9..4c83b5902 100644
--- a/internal/router/session.go
+++ b/internal/router/session.go
@@ -31,6 +31,7 @@ import (
 	"github.com/spf13/viper"
 	"github.com/superseriousbusiness/gotosocial/internal/config"
 	"github.com/superseriousbusiness/gotosocial/internal/db"
+	"golang.org/x/net/idna"
 )
 
 // SessionOptions returns the standard set of options to use for each session.
@@ -61,7 +62,14 @@ func SessionName() (string, error) {
 		return "", fmt.Errorf("could not derive hostname without port from %s://%s", protocol, host)
 	}
 
-	return fmt.Sprintf("gotosocial-%s", strippedHostname), nil
+	// make sure IDNs are converted to punycode or the cookie library breaks:
+	// see https://en.wikipedia.org/wiki/Punycode
+	punyHostname, err := idna.New().ToASCII(strippedHostname)
+	if err != nil {
+		return "", fmt.Errorf("could not convert %s to punycode: %s", strippedHostname, err)
+	}
+
+	return fmt.Sprintf("gotosocial-%s", punyHostname), nil
 }
 
 func useSession(ctx context.Context, sessionDB db.Session, engine *gin.Engine) error {
diff --git a/internal/router/session_test.go b/internal/router/session_test.go
index 40ceae346..d36da9596 100644
--- a/internal/router/session_test.go
+++ b/internal/router/session_test.go
@@ -82,6 +82,15 @@ func (suite *SessionTestSuite) TestDeriveSessionOK() {
 	suite.Equal("gotosocial-example.org", sessionName)
 }
 
+func (suite *SessionTestSuite) TestDeriveSessionIDNOK() {
+	viper.Set(config.Keys.Protocol, "https")
+	viper.Set(config.Keys.Host, "fóid.org")
+
+	sessionName, err := router.SessionName()
+	suite.NoError(err)
+	suite.Equal("gotosocial-xn--fid-gna.org", sessionName)
+}
+
 func TestSessionTestSuite(t *testing.T) {
 	suite.Run(t, &SessionTestSuite{})
 }