diff --git a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
index 539bf00a3627..e0878e65a488 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
@@ -268,6 +268,14 @@
dynamically.
+
+
+ Enabling global redirect in
+ services.nginx.virtualHosts now allows one
+ to add exceptions with the locations
+ option.
+
+
Resilio sync secret keys can now be provided using a secrets
diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md
index 6a6a34efbd14..e742570fc772 100644
--- a/nixos/doc/manual/release-notes/rl-2305.section.md
+++ b/nixos/doc/manual/release-notes/rl-2305.section.md
@@ -78,6 +78,8 @@ In addition to numerous new and upgraded packages, this release has the followin
- The new option `users.motdFile` allows configuring a Message Of The Day that can be updated dynamically.
+- Enabling global redirect in `services.nginx.virtualHosts` now allows one to add exceptions with the `locations` option.
+
- Resilio sync secret keys can now be provided using a secrets file at runtime, preventing these secrets from ending up in the Nix store.
- The `services.fwupd` module now allows arbitrary daemon settings to be configured in a structured manner ([`services.fwupd.daemonSettings`](#opt-services.fwupd.daemonSettings)).
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 85c76ed59d66..0b0e0de21df2 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -318,7 +318,9 @@ let
${acmeLocation}
${optionalString (vhost.root != null) "root ${vhost.root};"}
${optionalString (vhost.globalRedirect != null) ''
- return 301 http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri;
+ location / {
+ return 301 http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri;
+ }
''}
${optionalString hasSSL ''
ssl_certificate ${vhost.sslCertificate};
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 7163d0c8dc60..1956d3c9e8c7 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -435,6 +435,7 @@ in {
nginx = handleTest ./nginx.nix {};
nginx-auth = handleTest ./nginx-auth.nix {};
nginx-etag = handleTest ./nginx-etag.nix {};
+ nginx-globalredirect = handleTest ./nginx-globalredirect.nix {};
nginx-http3 = handleTest ./nginx-http3.nix {};
nginx-modsecurity = handleTest ./nginx-modsecurity.nix {};
nginx-njs = handleTest ./nginx-njs.nix {};
diff --git a/nixos/tests/nginx-globalredirect.nix b/nixos/tests/nginx-globalredirect.nix
new file mode 100644
index 000000000000..5f5f4f344d82
--- /dev/null
+++ b/nixos/tests/nginx-globalredirect.nix
@@ -0,0 +1,24 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+ name = "nginx-globalredirect";
+
+ nodes = {
+ webserver = { pkgs, lib, ... }: {
+ services.nginx = {
+ enable = true;
+ virtualHosts.localhost = {
+ globalRedirect = "other.example.com";
+ # Add an exception
+ locations."/noredirect".return = "200 'foo'";
+ };
+ };
+ };
+ };
+
+ testScript = ''
+ webserver.wait_for_unit("nginx")
+ webserver.wait_for_open_port(80)
+
+ webserver.succeed("curl --fail -si http://localhost/alf | grep '^Location:.*/alf'")
+ webserver.fail("curl --fail -si http://localhost/noredirect | grep '^Location:'")
+ '';
+})
diff --git a/pkgs/servers/http/nginx/generic.nix b/pkgs/servers/http/nginx/generic.nix
index a18b771aa1c3..7d301695cac0 100644
--- a/pkgs/servers/http/nginx/generic.nix
+++ b/pkgs/servers/http/nginx/generic.nix
@@ -176,7 +176,7 @@ stdenv.mkDerivation {
passthru = {
modules = modules;
tests = {
- inherit (nixosTests) nginx nginx-auth nginx-etag nginx-http3 nginx-pubhtml nginx-sandbox nginx-sso;
+ inherit (nixosTests) nginx nginx-auth nginx-etag nginx-globalredirect nginx-http3 nginx-pubhtml nginx-sandbox nginx-sso;
variants = lib.recurseIntoAttrs nixosTests.nginx-variants;
acme-integration = nixosTests.acme;
} // passthru.tests;