{"id":1343,"date":"2021-05-07T07:56:02","date_gmt":"2021-05-07T05:56:02","guid":{"rendered":"https:\/\/www.netexpertise.eu\/en\/?p=1343"},"modified":"2021-08-07T09:53:48","modified_gmt":"2021-08-07T07:53:48","slug":"nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash","status":"publish","type":"post","link":"http:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html","title":{"rendered":"Nginx Behind Reverse Proxy 301 https to http Redirect When URL has no Trailing Slash"},"content":{"rendered":"\n<p>An <a href=\"\/en\/category\/misc\/nginx\">Nginx<\/a> web server hosted on Kubernetes was sending back to me permanent 301 http redirects although I was sending https requests. The web server was behind a reverse proxy that was also running Nginx but it would be the same story with haproxy or another. Worst, the reverse proxy was redirecting http requests to https (this is normal behaviour) but without a trailing slash, creating a loop!<br><br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"\/images\/nginx_http_redirect.png\"><img decoding=\"async\" src=\"\/images\/nginx_http_redirect.png\" alt=\"Nginx http redirect behind proxy\"\/><\/a><\/figure>\n\n\n\n<p><br>Whenever I tried to connect to https:\/\/mydomain\/app, I got a redirect to http:\/\/mydomain\/app\/. And the other way around&#8230; A simple curl confirmed the redirect:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">$ curl -v <strong>https<\/strong>:\/\/mydomain\/app\n[...]\n &gt;&nbsp;\n * Connection state changed (MAX_CONCURRENT_STREAMS == 128)!\n &lt; HTTP\/2 301&nbsp;\n &lt; date: Thu, 06 May 2021 08:56:48 GMT\n &lt; content-type: text\/html\n &lt; content-length: 170\n &lt; <strong>location: http:\/\/mydomain\/app\/<\/strong><a href=\"http:\/\/pp-bred.izicap.com\/merchant\/\">\n<\/a> [...]\n &lt;html&gt;\n &lt;head&gt;&lt;title&gt;301 Moved Permanently&lt;\/title&gt;&lt;\/head&gt;\n &lt;body&gt;\n &lt;center&gt;&lt;h1&gt;301 Moved Permanently&lt;\/h1&gt;&lt;\/center&gt;\n &lt;hr&gt;&lt;center&gt;nginx\/1.19.10&lt;\/center&gt;\n &lt;\/body&gt;\n &lt;\/html&gt;<\/code><\/pre>\n\n\n\n<p><br>Nginx adds a trailing slash because that&#8217;s its default behaviour as describe on the <a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_core_module.html#location\">Nginx documentation<\/a>:<br><br>&#8220;In response to a request with URI equal to this string, but without the trailing slash, a permanent redirect with the code 301 will be returned to the requested URI with the slash appended.&#8221; If this is not desired, an exact match of the URI and location could be defined with 2 blocks:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"nginx\" class=\"language-nginx\">location \/app\/ {\n    [...]\n}\n\nlocation = \/app {\n    [...]\n}<\/code><\/pre>\n\n\n\n<p><br>Right&#8230; but this cannot be done on the root directory and you don&#8217;t want to do this for every single directory. Don&#8217;t expect developers to give you a ring every time they create a new directory!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><br>Accept URIs with and without a Trailing Slash<\/h2>\n\n\n\n<p>Nginx provides another way to accept URLs with or without a trailing slash, other than duplicating location blocks.<br><a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_core_module.html#try_files\">try_files<\/a> lets you do that by checking the existence of files in the specified order and uses the first found file to process the request.<br>It is even possible to check a directory\u2019s existence by appending a slash to $uri which gives the following configuration lines:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"nginx\" class=\"language-nginx\">location \/ {\n    try_files $uri $uri\/index.html $uri\/ =404;\n}<\/code><\/pre>\n\n\n\n<p><br>Sounds perfect but there&#8217;s a catch. Once I&#8217;ve reloaded Nginx, the web browser could fetch no more CSS and Javascript links.<br>Checking the code returned to the browser, CSS links have been changed from \/css\/my_file.css to \/my_file.css.<br><br>The problem is that <em>try_files<\/em> will source the file $uri\/index.html&nbsp;but leaves the URI as \/app&nbsp;so any relative URI will be relative to \/ (or the parent directory) and not \/app\/.<br><br>OK, that works but I need to change all the links to absolute path. From a developer point of view, I would understand this is not something they want.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><br>Relative Redirects<\/h2>\n\n\n\n<p>We want something cleaner but we can&#8217;t prevent Nginx&#8217;s default behaviour from appending a trailing slash.<br>Let&#8217;s focus on the https to http redirect. This is where the main problem lies and most browsers deny that kind of redirection&#8230; This is why the problem might be visible on curl but most browsers.<br><br>Nginx redirects to the full (or absolute) URL and a trailing slash. The definite solution is to do a relative redirect:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"nginx\" class=\"language-nginx\"><code>absolute_redirect off;<\/code><\/code><\/pre>\n\n\n\n<p><br>Reload nginx and run another test with curl:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">$ curl -v <strong>https<\/strong>:\/\/mydomain\/app\n[...]\n &gt;&nbsp;\n * Connection state changed (MAX_CONCURRENT_STREAMS == 128)!\n &lt; HTTP\/2 301&nbsp;\n &lt; date: Thu, 06 May 2021 08:56:48 GMT\n &lt; content-type: text\/html\n &lt; content-length: 170\n &lt; <strong>location: \/app\/<\/strong>\n [...]\n &lt;html&gt;\n &lt;head&gt;&lt;title&gt;301 Moved Permanently&lt;\/title&gt;&lt;\/head&gt;\n &lt;body&gt;\n &lt;center&gt;&lt;h1&gt;301 Moved Permanently&lt;\/h1&gt;&lt;\/center&gt;\n &lt;hr&gt;&lt;center&gt;nginx\/1.19.10&lt;\/center&gt;\n &lt;\/body&gt;\n &lt;\/html&gt;<\/code><\/pre>\n\n\n\n<p><br>Redirection location was http:\/\/mydomain\/app and is now \/app\/.<br>Doing that, the client will just reconnect with the protocol he used in the first request.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>An Nginx web server hosted on Kubernetes was sending back to me permanent 301 http redirects although I was sending https requests. The web server was behind a reverse proxy that was also running Nginx but it would be the same story with haproxy or another. Worst, the reverse proxy was redirecting http requests to [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[439],"tags":[443,442],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v21.8.1 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Netexpertise - Nginx Behind Reverse Proxy 301 https to http Redirect When URL has no Trailing Slash<\/title>\n<meta name=\"description\" content=\"Fix 301 https to http redirects on URLs with no trailing slash when Nginx is behind a SSL offloading reverse proxy\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Netexpertise - Nginx Behind Reverse Proxy 301 https to http Redirect When URL has no Trailing Slash\" \/>\n<meta property=\"og:description\" content=\"Fix 301 https to http redirects on URLs with no trailing slash when Nginx is behind a SSL offloading reverse proxy\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html\" \/>\n<meta property=\"og:site_name\" content=\"Netexpertise\" \/>\n<meta property=\"article:published_time\" content=\"2021-05-07T05:56:02+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2021-08-07T07:53:48+00:00\" \/>\n<meta name=\"author\" content=\"dave\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@netexpertise\" \/>\n<meta name=\"twitter:site\" content=\"@netexpertise\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html\",\"url\":\"https:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html\",\"name\":\"Netexpertise - Nginx Behind Reverse Proxy 301 https to http Redirect When URL has no Trailing Slash\",\"isPartOf\":{\"@id\":\"https:\/\/www.netexpertise.eu\/en\/#website\"},\"datePublished\":\"2021-05-07T05:56:02+00:00\",\"dateModified\":\"2021-08-07T07:53:48+00:00\",\"author\":{\"@id\":\"https:\/\/www.netexpertise.eu\/en\/#\/schema\/person\/cb4cd666549d22e9070ec1cfc1a496fa\"},\"description\":\"Fix 301 https to http redirects on URLs with no trailing slash when Nginx is behind a SSL offloading reverse proxy\",\"breadcrumb\":{\"@id\":\"https:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.netexpertise.eu\/en\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Nginx Behind Reverse Proxy 301 https to http Redirect When URL has no Trailing Slash\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.netexpertise.eu\/en\/#website\",\"url\":\"https:\/\/www.netexpertise.eu\/en\/\",\"name\":\"Netexpertise\",\"description\":\"Systems \/ Networks \/ DevOps\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.netexpertise.eu\/en\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.netexpertise.eu\/en\/#\/schema\/person\/cb4cd666549d22e9070ec1cfc1a496fa\",\"name\":\"dave\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.netexpertise.eu\/en\/#\/schema\/person\/image\/\",\"url\":\"http:\/\/1.gravatar.com\/avatar\/1129916e1f4955bd632f27f836f64e55?s=96&d=mm&r=g\",\"contentUrl\":\"http:\/\/1.gravatar.com\/avatar\/1129916e1f4955bd632f27f836f64e55?s=96&d=mm&r=g\",\"caption\":\"dave\"},\"sameAs\":[\"http:\/\/www.netexpertise.eu\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Netexpertise - Nginx Behind Reverse Proxy 301 https to http Redirect When URL has no Trailing Slash","description":"Fix 301 https to http redirects on URLs with no trailing slash when Nginx is behind a SSL offloading reverse proxy","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html","og_locale":"en_US","og_type":"article","og_title":"Netexpertise - Nginx Behind Reverse Proxy 301 https to http Redirect When URL has no Trailing Slash","og_description":"Fix 301 https to http redirects on URLs with no trailing slash when Nginx is behind a SSL offloading reverse proxy","og_url":"https:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html","og_site_name":"Netexpertise","article_published_time":"2021-05-07T05:56:02+00:00","article_modified_time":"2021-08-07T07:53:48+00:00","author":"dave","twitter_card":"summary_large_image","twitter_creator":"@netexpertise","twitter_site":"@netexpertise","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html","url":"https:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html","name":"Netexpertise - Nginx Behind Reverse Proxy 301 https to http Redirect When URL has no Trailing Slash","isPartOf":{"@id":"https:\/\/www.netexpertise.eu\/en\/#website"},"datePublished":"2021-05-07T05:56:02+00:00","dateModified":"2021-08-07T07:53:48+00:00","author":{"@id":"https:\/\/www.netexpertise.eu\/en\/#\/schema\/person\/cb4cd666549d22e9070ec1cfc1a496fa"},"description":"Fix 301 https to http redirects on URLs with no trailing slash when Nginx is behind a SSL offloading reverse proxy","breadcrumb":{"@id":"https:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.netexpertise.eu\/en\/misc\/nginx\/nginx-behind-reverse-proxy-301-https-to-http-redirect-when-url-has-no-trailing-slash.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.netexpertise.eu\/en"},{"@type":"ListItem","position":2,"name":"Nginx Behind Reverse Proxy 301 https to http Redirect When URL has no Trailing Slash"}]},{"@type":"WebSite","@id":"https:\/\/www.netexpertise.eu\/en\/#website","url":"https:\/\/www.netexpertise.eu\/en\/","name":"Netexpertise","description":"Systems \/ Networks \/ DevOps","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.netexpertise.eu\/en\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.netexpertise.eu\/en\/#\/schema\/person\/cb4cd666549d22e9070ec1cfc1a496fa","name":"dave","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.netexpertise.eu\/en\/#\/schema\/person\/image\/","url":"http:\/\/1.gravatar.com\/avatar\/1129916e1f4955bd632f27f836f64e55?s=96&d=mm&r=g","contentUrl":"http:\/\/1.gravatar.com\/avatar\/1129916e1f4955bd632f27f836f64e55?s=96&d=mm&r=g","caption":"dave"},"sameAs":["http:\/\/www.netexpertise.eu"]}]}},"_links":{"self":[{"href":"http:\/\/www.netexpertise.eu\/en\/wp-json\/wp\/v2\/posts\/1343"}],"collection":[{"href":"http:\/\/www.netexpertise.eu\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.netexpertise.eu\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.netexpertise.eu\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.netexpertise.eu\/en\/wp-json\/wp\/v2\/comments?post=1343"}],"version-history":[{"count":0,"href":"http:\/\/www.netexpertise.eu\/en\/wp-json\/wp\/v2\/posts\/1343\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.netexpertise.eu\/en\/wp-json\/wp\/v2\/media?parent=1343"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.netexpertise.eu\/en\/wp-json\/wp\/v2\/categories?post=1343"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.netexpertise.eu\/en\/wp-json\/wp\/v2\/tags?post=1343"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}