Full Answer
Nginx handles query strings in redirects differently from Apache. The default behaviour for a simple rewrite or return directive is to append the original query string to the redirect target automatically, which means a basic HTTP-to-HTTPS redirect like return 301 https://example.com$request_uri preserves UTM parameters without any additional configuration.
The risk emerges when a redirect target includes its own query string. If you write return 301 https://example.com/landing?page=home, Nginx replaces the original query string with page=home, discarding every utm_ parameter, gclid, and fbclid that was present. This is the same destructive behaviour as Apache without QSA, but it triggers under a different condition.
The fix is to explicitly include the original arguments. For rewrite directives, appending ?$args to the target preserves the original query string: rewrite ^/old-path$ /new-path?$args permanent. For return directives, using $request_uri instead of just the path handles both the path and query string as a single unit.
There is one additional subtlety. Nginx's proxy_pass directive has its own rules: if the proxy_pass target includes a URI path, query strings are not forwarded automatically. This matters for WooCommerce stores using reverse proxy configurations or caching layers in front of WordPress. Each layer in the request chain that transforms the URL is a potential point where UTM parameters can be silently dropped if the configuration does not explicitly carry query strings forward.