Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversHalloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
Skip to content
World desk10 min

How to Redirect HTTP to HTTPS in Nginx and Return a Proper 404

A practical Nginx configuration for permanent HTTP-to-HTTPS redirects, TLS setup, static routing and custom 404 pages that retain the correct status.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use separate Nginx server blocks: a port-80 block that returns 301 to the same HTTPS path, and a TLS-enabled port-443 block that serves the site. In the HTTPS block, make missing resources end with =404; use error_page 404 /404.html; to display a custom page without changing the response to a redirect or a successful 200.

The configuration below preserves the requested path and query string, supports a static site, and keeps the not-found status visible to browsers, crawlers and monitoring tools.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The request flow you want

A browser requesting http://example.com/docs/missing.html?lang=en should receive one permanent redirect:

  1. The port-80 Nginx server returns 301 Moved Permanently with a Location header pointing to https://example.com/docs/missing.html?lang=en.
  2. The browser makes the HTTPS request.
  3. The TLS-enabled server checks the path. If the resource does not exist, Nginx returns 404 Not Found and renders your custom error document.

The redirect and the 404 are separate decisions. The first response changes the transport scheme; it must not turn an absent resource into a success page. The second response describes whether the requested HTTPS resource exists.

Why two server blocks are the clearest design

HTTP and HTTPS are different listeners with different requirements. The port-80 block only needs to receive insecure traffic and issue the permanent redirect. The port-443 block needs a TLS listener, a certificate, a private key, a document root or upstream application, and the routing rules that determine whether a request exists.

Keeping those jobs separate avoids several common errors:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • A redirect inside the HTTPS content location can interfere with application routing or create a redirect loop.
  • A port-80 block cannot serve an HTTPS response until the browser reconnects over TLS.
  • A redirect rule does not install or validate a certificate. The 443 server still needs working certificate and key paths.
  • Not-found handling belongs in the HTTPS server that ultimately serves the content, not in the initial scheme redirect.

Prerequisites before editing Nginx

  • A DNS record for the hostname or hostnames that should resolve to this server.
  • A certificate whose names cover every HTTPS hostname you intend to serve, plus the matching private key.
  • Readable certificate and key files. Protect the private key with filesystem permissions appropriate to your deployment.
  • A document root containing your site and a real 404.html file, or an application route that deliberately emits a 404.
  • Permission to validate and reload Nginx, commonly through the service manager used by your operating system.

Replace example.com, the certificate paths and /var/www/html with values from your installation. Do not copy a certificate path from an example unless that file actually exists on your server.

Step 1: Create the HTTP-to-HTTPS redirect block

Put this server block in the Nginx configuration that is loaded for your site:

server {
    listen 80;
    server_name example.com www.example.com;

    return 301 https://$host$request_uri;
}

return 301 ends processing immediately and sends a permanent redirect. $request_uri contains the original path and query string, so a request such as /products?id=42 is redirected to the same path and query over HTTPS.

$host uses the host associated with the request. This is useful when the same block intentionally serves more than one hostname and you want to preserve whichever name was requested. If your site has one canonical hostname, use that fixed name instead:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
server {
    listen 80;
    server_name example.com www.example.com;

    return 301 https://example.com$request_uri;
}

A fixed hostname is appropriate when, for example, www.example.com must always canonicalize to example.com. It also makes the redirect destination explicit. Whichever form you choose, list only hostnames that this server is meant to answer for.

Step 2: Configure the TLS-enabled HTTPS server

The HTTPS block must listen on 443 with SSL enabled and point to the certificate and private key:

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    ssl_certificate     /etc/nginx/ssl/example.crt;
    ssl_certificate_key /etc/nginx/ssl/example.key;

    root /var/www/html;
    index index.html;

    location / {
        try_files $uri $uri/ $uri.html =404;
    }

    error_page 404 /404.html;
    location = /404.html {
        internal;
    }
}

The root directive establishes the filesystem base for static files. The index directive identifies the default file for a directory request. Adjust both to match your site.

The try_files directive checks candidates in order:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. $uri checks the requested path as a file.
  2. $uri/ checks whether it is a directory, allowing the index handling to run.
  3. $uri.html supports extensionless URLs backed by files such as about.html.
  4. =404 is the final action when none of those candidates exists.

That final parameter is essential. Without an explicit not-found result, another location, framework fallback or upstream application may generate a page with a successful status for an unknown URL.

Step 3: Add a custom 404 document without changing the status

Create an actual file at /var/www/html/404.html. It can contain your site navigation, a concise explanation and links to useful destinations. Keep it self-contained enough to render when the original resource is absent.

Then use:

error_page 404 /404.html;
location = /404.html {
    internal;
}

The plain error_page 404 /404.html; form performs an internal redirect to the custom document while retaining the 404 semantics for the client response. The exact-match location serves the file only during Nginx’s internal error processing. The internal directive prevents a visitor from requesting the handler URI directly as a normal public endpoint.

Do not replace that line with:

error_page 404 =301 https://example.com/404.html;

The =301 form deliberately changes the client-visible result into a redirect. A browser may then show a 301 followed by a page that returns 200, and crawlers will no longer receive a direct 404 for the missing URL. Use the redirect form only when redirecting not-found requests is genuinely your intended behavior.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Step 4: Put the file in the active configuration and reload safely

The exact include directory differs by operating system and package, but the sequence is the same:

  1. Save the server blocks in a file that the main Nginx configuration includes.
  2. Check that the certificate, key, document root and 404.html paths are correct.
  3. Validate syntax before touching the running service:
sudo nginx -t

Proceed only when the test reports that the configuration syntax is successful and the configuration test is successful. If it reports an unknown directive, missing file or permission error, correct that problem first.

  1. Reload Nginx so existing connections can finish while workers adopt the new configuration:
sudo systemctl reload nginx

If your system does not use systemctl, use its Nginx service-management command instead. A reload cannot compensate for a certificate that is invalid, expired or issued for a different hostname; those TLS problems must be fixed separately.

How to verify both the redirect and the real 404

Test each stage independently. Using curl makes the status and headers visible without relying on browser UI:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Check the HTTP redirect

curl -I 'http://example.com/missing-page?ref=test'

Expect a single 301 response and a Location header containing the HTTPS scheme, the intended hostname, /missing-page and ref=test. If the path or query is missing, inspect the redirect expression and make sure it uses $request_uri.

Check the HTTPS missing resource

curl -i 'https://example.com/missing-page?ref=test'

The final response should have status 404 and the body of your custom page. It should not require a second redirect to the error document. A certificate warning during this test means the TLS setup is not ready for normal clients; resolve that before interpreting application routing results.

Check an existing file

curl -I 'https://example.com/index.html'

An existing file should return its normal successful status. This confirms that the 404 rule is not catching every request.

Inspect the complete redirect chain when needed

curl -IL 'http://example.com/missing-page?ref=test'

The chain should show the HTTP 301 followed by one HTTPS response with status 404. Additional 301 or 302 responses usually indicate canonical-host rules, application routing or proxy behavior that needs review.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choosing 301, 302, 307 or 308

The permanent 301 in the standard pattern tells clients and caches that HTTP should no longer be used for this resource. It is the usual choice after HTTPS is configured and tested.

Status Typical use Method behavior What to consider
301 Permanent HTTP-to-HTTPS migration Clients may rewrite non-GET methods according to their redirect handling Widely supported and cacheable; clear old cached redirects when testing changes
302 Temporary redirect during a short-lived trial Historically treated like a GET by many clients Does not communicate a permanent canonical scheme
307 Temporary redirect when the request method and body must be preserved Preserves the method Useful for temporary testing of APIs or non-GET requests
308 Permanent redirect when method preservation is required Preserves the method Confirm that every client and intermediary in your audience handles it correctly

For an ordinary website whose HTTP traffic is being permanently moved to HTTPS, 301 is the documented, broadly compatible choice. Select another status only for a specific temporary or method-preservation requirement.

Canonical host: $host or a fixed name

The redirect destination must also express your hostname policy. With https://$host$request_uri, a request for either name listed in server_name remains on that name. This is convenient for multi-host configurations, but it does not merge those names into one canonical URL.

With https://example.com$request_uri, every matching HTTP request goes to example.com. Use this when one hostname is canonical and the other is only an alias. If you need different canonical rules for different names, create separate server blocks rather than adding complicated conditionals to one redirect.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Whichever policy you choose, test both the bare and www forms, including a path with a query string. Canonicalization should happen predictably and should not add a second redirect after the scheme change.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Static sites versus application routing

Static files

The configuration shown above is designed for a static site. try_files checks the filesystem and ends with =404 when no candidate exists. The custom page is another static file below the configured root.

Front-controller applications

Many applications route every URL through one entry script. In that design, the Nginx location often uses a fallback such as try_files $uri $uri/ /index.php?$query_string; or proxies to an upstream. A fallback is not automatically wrong, but the application must decide whether the route exists and send a genuine 404 for an unknown route.

Do not assume that displaying a “not found” message is enough. A framework can render that message with status 200, which misleads crawlers, monitoring systems and clients. Verify the actual status with curl -i, and configure the application or upstream to emit 404 when it cannot resolve the requested resource.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Proxying to an upstream service

If the HTTPS server uses proxy_pass, decide where not-found behavior belongs. An upstream that returns 404 can normally pass that status through. If it returns a branded error page with 200, Nginx cannot infer that the content is missing; fix the upstream response or add a carefully scoped rule that handles the known absence condition.

Be cautious with proxy_intercept_errors and shared error pages. Intercepting an upstream error can be useful, but it must not accidentally convert the status. Test both an upstream 404 and an upstream 500 so that the custom 404 document is used only for not-found responses.

When a load balancer terminates TLS before Nginx

In some deployments, a load balancer, reverse proxy or ingress device accepts HTTPS from the client and forwards a request to Nginx over an internal connection. In that architecture, Nginx may receive an HTTP connection even though the original client used HTTPS.

Do not blindly place an HTTP-to-HTTPS redirect on every request that arrives at Nginx. That can create a loop: the edge redirects to HTTPS, the edge forwards internally over HTTP, and Nginx redirects again. Instead, configure the edge to provide its trusted forwarded-protocol signal and make Nginx honor it only from that trusted proxy network. The exact header name, address trust list and configuration depend on the proxy product and your network design.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If Nginx terminates TLS directly, the two-block configuration remains straightforward: clients reach port 80 for the redirect and port 443 for certificate validation and content delivery.

Common failure modes and fixes

Redirect loop

  • Check whether a proxy already terminated TLS and whether Nginx knows the original scheme.
  • Confirm that the port-443 server does not contain a blanket redirect back to HTTPS.
  • Check the complete chain with curl -IL and identify which hop repeats.

Certificate error after the redirect

  • Confirm that the 443 block is loaded and that its server_name matches the requested host.
  • Verify the certificate covers that host and that the key matches the certificate.
  • Run sudo nginx -t to catch unreadable or missing certificate files.

Missing URL returns 200

  • Inspect try_files and ensure its final parameter is =404.
  • Look for a front-controller or proxy fallback that handles every path.
  • Make the application emit 404 for an unresolved route and retest with curl -i.

Custom page itself redirects

  • Ensure /404.html is a real file below the configured root.
  • Use the internal error-page form, not =301 or another external redirect.
  • Check that a broad location rule is not intercepting the exact /404.html location.

Query string disappears

  • Use $request_uri in the port-80 return directive.
  • Do not reconstruct the destination from only $uri unless dropping the query string is intentional.

Direct request to the error URI behaves unexpectedly

The internal directive intentionally rejects an ordinary client request to /404.html. That is expected. Nginx can still use the file for its internal 404 processing.

A complete static-site example

This assembled configuration shows the two listeners and the custom error handler together:

server {
    listen 80;
    server_name example.com www.example.com;

    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    ssl_certificate     /etc/nginx/ssl/example.crt;
    ssl_certificate_key /etc/nginx/ssl/example.key;

    root /var/www/html;
    index index.html;

    location / {
        try_files $uri $uri/ $uri.html =404;
    }

    error_page 404 /404.html;
    location = /404.html {
        internal;
    }
}

After replacing the example values, validate with nginx -t, reload Nginx, and test an existing URL, an unknown HTTPS URL and the same unknown URL over HTTP. The expected sequence for the last test is one 301 followed by one 404.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Operational checklist

  • Port 80 has a dedicated server block with one permanent return rule.
  • The redirect destination uses the intended canonical hostname.
  • $request_uri preserves the original path and query string.
  • Port 443 listens with TLS and points to a valid certificate and matching key.
  • The HTTPS content location ends missing static files with =404.
  • A real 404.html exists below the configured root.
  • error_page 404 /404.html; is internal, not an external 301.
  • Applications and upstreams return 404 rather than a generated 200 for unknown routes.
  • Proxy-terminated TLS uses a trusted forwarded-protocol signal to avoid loops.
  • nginx -t passes before every reload, and both status codes have been checked with curl.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.