diff --git a/src/gitlab-tofu.sh b/src/gitlab-tofu.sh
index aa62e139e4c3d6a34ccedc075806ff2a8bad302d..d168c5814b4defaa1791f53bc275c465bb29a203 100644
--- a/src/gitlab-tofu.sh
+++ b/src/gitlab-tofu.sh
@@ -8,6 +8,14 @@ if [ "${DEBUG_OUTPUT}" = "true" ]; then
     set -x
 fi
 
+# Feature Flags
+# =============
+# Below are a bunch of variables that we use as "feature flags".
+
+if [ -z "$TF_FF_AUTO_URLENCODE_STATE_NAME" ]; then
+  TF_FF_AUTO_URLENCODE_STATE_NAME=true
+fi
+
 # Helpers
 
 # Evaluate if this script is being sourced or executed directly.
@@ -45,8 +53,13 @@ if [ -z "${TF_PASSWORD}" ]; then
 fi
 
 # If TF_ADDRESS is unset but TF_STATE_NAME is provided, then default to GitLab backend in current project
-if [ -n "${TF_STATE_NAME}" ]; then
-  TF_ADDRESS="${TF_ADDRESS:-${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${TF_STATE_NAME}}"
+if [ -n "${TF_STATE_NAME}" ] && [ -z "${TF_ADDRESS}" ]; then
+  # auto url-encode TF_STATE_NAME when FF is enabled
+  if $TF_FF_AUTO_URLENCODE_STATE_NAME; then
+    TF_STATE_NAME="$(jq -rn --arg x "${TF_STATE_NAME}" '$x|@uri')"
+  fi
+
+  TF_ADDRESS="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${TF_STATE_NAME}"
 fi
 
 # If TF_ROOT is set then use the -chdir option
diff --git a/tests/unit.gitlab-ci.yml b/tests/unit.gitlab-ci.yml
index 7490a0180eee13fe4106b7506474cab64a98e1df..c51ee83f5f91e670772398b90f59bad1d70d3925 100644
--- a/tests/unit.gitlab-ci.yml
+++ b/tests/unit.gitlab-ci.yml
@@ -160,7 +160,7 @@ gitlab-tofu-init-with-prepared-registry-token:
     - |
       cat <<'EOF' > test.sh
       set -x
-      # NOTE: as part of the tst fixture, we need to overwrite the CI_SERVER_HOST,
+      # NOTE: as part of the test fixture, we need to overwrite the CI_SERVER_HOST,
       # so that this test also properly works on GitLab self-managed.
       export CI_SERVER_HOST=gitlab.example.com
       export TF_TOKEN_gitlab_example_com=mysecrettoken
@@ -189,7 +189,7 @@ gitlab-tofu-init-without-prepared-registry-token:
     - |
       cat <<'EOF' > test.sh
       set -x
-      # NOTE: as part of the tst fixture, we need to overwrite the CI_SERVER_HOST,
+      # NOTE: as part of the test fixture, we need to overwrite the CI_SERVER_HOST,
       # so that this test also properly works on GitLab self-managed.
       export CI_SERVER_HOST=gitlab.example.com
       . $(which gitlab-tofu)
@@ -340,3 +340,54 @@ gitlab-tofu-no-wrapper:
     - cat /tmp/output.txt
     - test $FAILED = true
     - 'grep "Error: Backend initialization required, please run \"tofu init\"" /tmp/output.txt'
+
+gitlab-tofu-state-name-auto-urlencode:
+  extends:
+    - .gitlab-tofu-test
+  stage: test
+  variables:
+    OPENTOFU_VERSION: $LATEST_OPENTOFU_VERSION
+  script:
+    - apk add --update $PKG
+    - |
+      cat <<'EOF' > test.sh
+      set -x
+      export TF_STATE_NAME=production/europe
+      . $(which gitlab-tofu)
+      test "$TF_STATE_NAME" = "production%2Feurope"
+      EOF
+    - $SHELL test.sh
+  parallel:
+    matrix:
+      - SHELL: "bash"
+        PKG: "bash"
+      - SHELL: "zsh"
+        PKG: "zsh"
+      - SHELL: "ksh"
+        PKG: "loksh"
+
+gitlab-tofu-state-name-auto-urlencode-ff-disabled:
+  extends:
+    - .gitlab-tofu-test
+  stage: test
+  variables:
+    OPENTOFU_VERSION: $LATEST_OPENTOFU_VERSION
+  script:
+    - apk add --update $PKG
+    - |
+      cat <<'EOF' > test.sh
+      set -x
+      export TF_FF_AUTO_URLENCODE_STATE_NAME=false
+      export TF_STATE_NAME=production/europe
+      . $(which gitlab-tofu)
+      test "$TF_STATE_NAME" = "production/europe"
+      EOF
+    - $SHELL test.sh
+  parallel:
+    matrix:
+      - SHELL: "bash"
+        PKG: "bash"
+      - SHELL: "zsh"
+        PKG: "zsh"
+      - SHELL: "ksh"
+        PKG: "loksh"