27 min read

Debug Next.js OAuth Behind an HTTPS Tunnel

Troubleshoot OAuth redirect URIs, cookies, forwarded headers, callback loops, and Docker routing in Next.js with a Localtonet HTTPS tunnel.

OAuth request and callback path through an HTTPS tunnel to a containerized Next.js app.
The redirect must cross the public HTTPS endpoint, tunnel, host, and container before Next.js can complete the OAuth session.
Development · Next.js OAuth · Localtonet · 2026

Make the public OAuth origin and the local Next.js application agree

An OAuth flow can work on localhost yet fail as soon as an identity provider redirects through a public HTTPS tunnel. The usual cause is not the tunnel itself, but disagreement between the registered callback URI, the application’s idea of its external origin, proxy headers, cookie scope, or the address used to reach a containerized Next.js process. This guide presents a systematic way to isolate those problems without guessing version-specific Next.js, Auth.js, or provider settings. We also show how to expose the verified local application through a Localtonet HTTP tunnel as a distinct integration step.

🔒 Diagnose state, nonce, PKCE, and secure-cookie failures 🌐 Keep public HTTPS callbacks consistent across every OAuth layer ⚡ Separate application, proxy, provider, and Docker routing problems

Understand the OAuth request path before changing settings

A tunneled OAuth flow crosses several boundaries. The browser begins at a public HTTPS address, the authorization request goes to the identity provider, the provider redirects the browser to a registered callback URI, and the tunnel carries that callback to a Next.js service listening on a local interface or inside a container. The application then validates temporary OAuth data, exchanges the authorization code where required, creates a session, and redirects the browser again.

Every participant must agree on the externally visible origin. In this article, public origin means the scheme and authority visible to the browser, such as https://public-name.example. The complete callback URI also includes the callback path expected by the authentication implementation. A mismatch in scheme, host, port, path, slash handling, or letter case can be significant because OAuth providers generally compare redirect URIs strictly.

The local target is different. It is the IP address and port where the Localtonet client can reach Next.js. It might use plain HTTP even though the browser uses HTTPS, because these values describe different legs of the request. The public callback must not be rewritten to the local target merely because Next.js ultimately receives the request there.

🌐 Public browser origin This is the HTTPS origin assigned to the HTTP tunnel. The browser and identity provider use it for the externally reachable callback.
↩️ Registered callback URI This exact URI is configured with the OAuth provider. It combines the public origin with the callback path required by the installed authentication implementation.
🧭 Application origin detection Next.js or the authentication library must determine the external host and protocol correctly when generating redirects and validating requests.
🍪 Temporary browser cookies State, nonce, PKCE, and related cookies must survive the round trip and return to the callback under compatible domain, path, SameSite, and Secure rules.
📦 Local or container target The tunnel client needs a reachable local IP address and port. A service that works only from inside its container is not necessarily reachable from the host.
🔍 Observed request data Logs, browser network details, cookies, response locations, and actual incoming headers provide stronger evidence than repeatedly changing configuration.
Keep the external and internal addresses separate

The public HTTPS URL is the identity-provider-facing callback origin. The local IP and port are only the tunnel target. They do not need the same scheme or hostname, but the application must still understand that the original browser request used the public HTTPS origin.

Collect the exact values and version-specific requirements

Do not begin by copying environment variables from an unrelated tutorial. Next.js authentication integrations have changed over time, providers expose different console interfaces, and trusted-host or proxy behavior can depend on the installed authentication-library version. The private evidence supplied for this draft did not establish a specific Next.js release, Auth.js release, provider, callback route, environment-variable name, or trusted-host option. We therefore do not invent them.

Before troubleshooting, record the following values from the application and the current official documentation for the versions actually installed:

  • The Next.js version and whether the relevant authentication route uses the App Router or Pages Router.
  • The authentication package name and exact installed version.
  • The OAuth provider and the provider’s currently registered callback URI.
  • The callback path implemented by the installed package or custom route.
  • The local listening address and port used by the running Next.js process.
  • Whether Next.js runs directly on the client device, in Docker, or behind another local reverse proxy.
  • The public HTTPS URL assigned to the Localtonet tunnel after it starts.
  • The documented mechanism, if any, for setting the external application URL or trusting proxy-derived host information.
  • The expected cookie names and attributes as observed in the browser, rather than inferred from another package version.

If a package manager lockfile is present, use it to identify the installed authentication version. If the application uses custom OAuth code, inspect the route that creates the authorization request and the route that receives the callback. In either case, determine where the callback URI comes from. It may be fixed in configuration, built from an application base URL, inferred from request headers, or supplied explicitly to the provider client.

Value Where to confirm it Why it matters
Public origin The running Localtonet HTTP tunnel It supplies the HTTPS host used by the browser and provider callback.
Callback path The installed authentication package documentation or application route A correct host with the wrong path still produces a redirect mismatch or an application 404.
Registered redirect URI The OAuth provider’s application configuration The provider compares the authorization request against its allowed redirect configuration.
External base URL behavior Current documentation for the exact authentication-library version It controls generated callbacks, redirects, host validation, or cookie behavior in some implementations.
Local target The running process, container publishing configuration, or local reverse proxy The Localtonet client must be able to open a connection to this IP address and port.
Forwarded request data Sanitized application-side request inspection It reveals whether the application receives enough information to reconstruct the public origin.
Do not expose secrets while collecting evidence

Never paste provider client secrets, Localtonet device tokens, authorization codes, session cookies, state values, nonce values, or private callback query strings into tickets or public logs. Record names, status codes, origins, paths, and redacted attributes instead.

Verify the Next.js application before adding remote access

The first useful boundary is the application itself. Confirm that the expected local service is running and that the Localtonet client device can reach it. A process merely appearing in a task list is not enough. It must listen on the intended address and port, and the authentication route must exist in the running build.

Test the local HTTP service

Set LOCAL_URL to the actual local URL already used by the application. Do not assume a framework default port.

export LOCAL_URL="http://LOCAL_IP:LOCAL_PORT"
curl -i "$LOCAL_URL"

A successful response proves only that the selected route is reachable. Next, test a harmless authentication entry route or callback route according to the installed implementation. A callback without provider parameters may intentionally return an error, but it should reach the expected Next.js handler rather than a different service, a container gateway error, or a generic 404.

If the service cannot be reached locally, stop here. Fix the listening address, local firewall policy, process state, port selection, container publishing, or reverse-proxy route before creating a public tunnel. OAuth configuration cannot repair a TCP connection that never reaches Next.js.

Capture a clean browser baseline

Use a fresh private browsing session or clear only the cookies associated with the test host. Start the sign-in flow once and inspect the browser’s network panel. Record:

  • The first application URL used to initiate sign-in.
  • The authorization request’s redirect_uri value, with sensitive values redacted.
  • The provider response status and redirect location.
  • The callback request host, path, status code, and response location.
  • Which temporary cookies are created before leaving the application.
  • Whether those cookies are attached to the callback request.

Avoid mixing local and public origins during a single test. If sign-in starts at a localhost URL but the provider returns to a public tunnel hostname, host-scoped cookies created on localhost will not automatically belong to the tunnel hostname. Start the entire test from the public URL once the tunnel is configured.

Expose the verified Next.js service with a Localtonet HTTP tunnel

With Localtonet, the client on the device establishes an outbound connection to one of our relay servers. This exposes the local service without inbound router port forwarding, firewall changes, VPN setup, or a public IP address. For a browser-based Next.js application, use an HTTP tunnel so the public side receives an HTTPS address while the tunnel points to the local IP address and port.

HTTP tunnels support Random Sub Domain, Custom Sub Domain, and Custom Domain process types. All three serve the same target content at a public HTTPS address. Availability can vary, so use only the choices currently presented for your account. Exact custom-domain DNS instructions are intentionally not included here because they must be checked against current Localtonet documentation.

1

Install and run the Localtonet client

Run our client on the device that can already reach the verified Next.js local URL. If Next.js runs in Docker, this is usually the device from which the published container port can be tested successfully.

2

Authenticate or select the client device

Use the device-specific authentication token associated with that client. Treat the token as a secret and never place it in source code, screenshots, logs, or this article’s example commands.

3

Select an available relay server

Choose from the server or region values currently available in the Localtonet dashboard. Do not copy a hardcoded server code from an old tutorial because available values can change.

4

Create an HTTP tunnel configuration

Select the desired process type and configure the local target IP address and port that passed the earlier reachability test. The target must identify Next.js or the intentional local reverse proxy in front of it.

5

Start the tunnel

Creating a tunnel does not start it. Use the Start button, then wait until the selected client is connected and the tunnel is running.

6

Open and record the assigned public HTTPS URL

Load the assigned URL in a browser and confirm that it reaches the expected Next.js application. This exact public origin is the value that must remain consistent through the OAuth authorization and callback flow.

For the current dashboard workflow, consult our Localtonet documentation alongside the fields shown in your account. Tunnel configuration and application authentication configuration are separate tasks. A tunnel can deliver requests correctly even while the OAuth provider rejects the callback URI, and an OAuth configuration can be correct while the local target remains unreachable.

A created tunnel is not necessarily available

The tunnel works only while the selected client device is connected and the tunnel is running. If a previously working callback suddenly becomes unreachable, check tunnel and device state before changing the OAuth configuration.

Align the redirect URI, application origin, and forwarded request information

Matching OAuth redirect URI, public origin, and forwarded host and protocol values.
Scheme, host, and callback path must describe the same public HTTPS request.

After the public application loads, construct the callback URI using two verified pieces: the assigned public HTTPS origin and the exact callback path required by the installed authentication implementation. Register that complete URI with the OAuth provider. Then verify what the application actually sends as redirect_uri in the authorization request.

These values should describe the same callback:

  • The redirect URI registered in the provider console.
  • The redirect_uri sent in the authorization request.
  • The callback location to which the provider redirects the browser.
  • The external origin expected by Next.js or the authentication library.
  • Any allowlist or trusted-host setting required by the installed version.

Diagnose a provider-side redirect URI mismatch

A provider-side error usually occurs before the provider sends the browser back to Next.js. Compare the complete URI character by character. Pay particular attention to http versus https, an old tunnel hostname, an explicit port, the callback path, a trailing slash, and accidental whitespace in configuration.

Do not solve this by registering a broad or guessed set of callbacks. Register the precise public callback required for the active environment. If the provider has separate development and production applications, keep their credentials and callback registrations clearly separated.

Diagnose an incorrect callback generated by the application

If the authorization request contains localhost, an internal container hostname, plain HTTP, or a stale public hostname, the application is constructing its external URL incorrectly. Determine whether the installed authentication package expects an explicit application URL, derives it from the incoming request, or supports a documented trusted-proxy mode.

Use only the configuration name documented for the exact installed version. Auth.js and related integrations have version-specific behavior, and this draft has no verified evidence for one universal environment variable or trusted-host switch. Adding a plausible but unsupported variable can conceal the real problem while leaving the application on its default behavior.

Inspect forwarded host and protocol carefully

Reverse proxies commonly communicate the original host and scheme through forwarded request headers. However, this article does not claim a specific Localtonet header set because that detail is not established by the supplied product context. Inspect what the application actually receives in your environment.

Add temporary, sanitized server-side diagnostics around the authentication route if the framework and deployment permit it. Log only the request URL, host-related header names and sanitized values, protocol-related header names and values, and route path. Never log cookies, authorization headers, OAuth codes, state, nonce, or tokens.

If another reverse proxy sits between the Localtonet target and Next.js, that proxy may overwrite, append, or discard forwarded information. Test each hop independently. First target Next.js directly if that is safe and practical. Then reintroduce the local proxy. A result that changes when the local proxy is added points to that proxy’s host or protocol handling rather than to the provider.

Observed symptom Most useful first check Likely boundary
The provider rejects the authorization request immediately Compare the sent redirect_uri with the registered URI Provider registration or generated callback
The generated callback uses HTTP instead of HTTPS Inspect external-origin configuration and received protocol information Application or proxy origin detection
The callback uses localhost or a container hostname Identify where the authentication library gets its base URL Application configuration
The application reports an untrusted host Check current package documentation for its host-trust mechanism Authentication-library validation
The public URL returns a gateway or connection error Test the configured local target from the client device Tunnel-to-application routing
Do not trust arbitrary forwarded headers globally

Host and protocol information influences redirects, cookie security, and origin checks. Enable proxy trust only through the documented mechanism for the installed framework or authentication version, and only when requests arrive through infrastructure you control and intend to trust.

Debug missing state, nonce, PKCE, and session cookies

Browser developer tools tracing OAuth callback cookies, state, nonce, and PKCE data.
A callback loop often begins when the browser does not return the cookie that holds OAuth validation data.

OAuth implementations use temporary browser state to bind the callback to the sign-in attempt. Depending on the provider and library, this can include state, nonce, and PKCE verifier data. If the callback reaches Next.js but the corresponding cookie is absent, expired, scoped to another host or path, or rejected by the browser, the application may report a state or nonce error, fail the PKCE exchange, or restart sign-in.

Start and finish on one public hostname

The cleanest rule is to initiate the OAuth flow from the same public hostname that receives the callback. Opening the application at localhost and returning through the tunnel crosses hostname boundaries. A host-only cookie created for localhost is not sent to the public tunnel hostname.

Tunnel hostnames can also change when a tunnel is recreated or when a different process type is selected. If the public hostname changes, update the provider registration and the application’s verified external-origin configuration, then start a new private browsing session. Old cookies and cached redirects can otherwise make the new test look inconsistent.

Inspect attributes rather than guessing cookie names

In the browser’s storage and network tools, identify the temporary cookie set immediately before the redirect to the provider. Record its domain or host scope, path, Secure status, SameSite value, expiry, and whether the browser reports that it was blocked. On the callback request, confirm whether the browser sends it.

A Secure cookie requires an HTTPS browser context. The Localtonet HTTP tunnel provides a public HTTPS address for this workflow, but the application must still recognize the external request context correctly when deciding how to create cookies. If the application incorrectly believes the external origin is HTTP, its generated cookie or redirect behavior may not match the public flow.

SameSite behavior depends on the exact navigation and cookie configuration. Do not force a particular SameSite value based on a generic tutorial. Use the authentication library’s documented defaults for the installed version unless there is a verified need to override them. If an override is necessary, evaluate the security effect as well as whether it makes the callback work.

Recognize the common callback loop

A loop often looks like this: the callback reaches the application, the application cannot validate temporary state or establish a session, it redirects to sign-in, and the cycle begins again. The first failed callback is the important request. Later requests are consequences.

Inspect the first callback response and ask:

  • Did the callback request contain the expected temporary cookie?
  • Did the callback reach the correct route and application instance?
  • Did the application calculate the expected public origin?
  • Was a session cookie returned, and did the browser accept it?
  • Did the response redirect to the public hostname or switch to localhost, HTTP, or an old hostname?
  • Are multiple application instances using compatible secret and session configuration?

Clear stale cookies only after preserving enough evidence to identify why the browser rejected or omitted them. Repeatedly clearing cookies can temporarily hide a scope problem without explaining it.

Fix callbacks that reach the tunnel but not the Next.js container

Docker routing comparison for an OAuth callback forwarded from the tunnel to Next.js.
The tunnel can reach the host while the callback still fails at the Docker port or container-listening boundary.

Docker introduces a separate routing question: which address is reachable from the device running the Localtonet client? A container’s internal address, the host’s loopback interface, a published host port, and a service name on a container network are not interchangeable.

The safest target is the exact host-side address and port that already succeeds when tested from the Localtonet client’s environment. If the client runs on the Docker host, verify that the container publishes the intended application port to an appropriate host interface. If the Localtonet client itself runs in another container, both containers need an intentional network path, and a host loopback address inside one container does not refer to the other container.

Separate bind failures from tunnel failures

A Next.js process may be healthy inside its container while listening only on an interface that is not reachable through the published path. Conversely, Docker may publish a port but direct it to the wrong internal port. Use the project’s actual startup configuration and container definition to confirm both sides. This article does not prescribe a port or listening-host flag because no version-specific application configuration was supplied.

Test from progressively closer locations:

  1. From inside the application container, request the application’s actual listening endpoint.
  2. From the Docker host, request the published host address and port.
  3. From the Localtonet client environment, request the precise target configured in the tunnel.
  4. Only after all three succeed, test the public HTTPS URL.

If step one fails, investigate Next.js startup. If step one succeeds but step two fails, investigate the container bind or port publication. If step two succeeds but step three fails, investigate which network namespace contains the Localtonet client. If all local tests succeed but the public request fails, check the selected device, tunnel state, target values, and whether the request is reaching the expected service.

Loopback is relative to its network namespace

An address such as 127.0.0.1 refers to the current host or container. When the Localtonet client and Next.js run in different containers, configuring loopback as the target generally points back to the client container, not to the Next.js container. Use an address that is demonstrably reachable from the client environment.

Use a boundary-by-boundary diagnostic workflow

The fastest investigation changes one variable at a time and identifies the first boundary that fails. Do not simultaneously replace the callback URI, change cookie flags, add proxy-trust options, recreate the tunnel, and modify Docker networking. That produces a working or broken result without explaining which change mattered.

1

Prove the local application route exists

Test the actual local target from the Localtonet client device. Confirm that the intended Next.js build is running and that the authentication route resolves to the expected handler.

2

Prove the public HTTPS route reaches that application

Start the Localtonet tunnel and load a distinctive application route through its public URL. Compare the response with the local result so that a different service or stale container is not mistaken for the target.

3

Inspect the outgoing authorization request

Start sign-in from the public URL and inspect the exact redirect_uri. If it is wrong, fix application origin generation before investigating callback cookies.

4

Compare the provider registration exactly

Confirm that the provider allows the precise public HTTPS callback. Save the provider configuration and begin a new authorization attempt rather than reusing an old authorization URL.

5

Follow the first callback request

Record its host, path, status, redirect location, and whether the expected temporary cookies were sent. Determine whether failure occurs before the request reaches Next.js or during callback validation.

6

Verify origin and proxy interpretation

Compare the public URL with the origin inferred by the application. Check current version documentation before enabling any external-base-URL or host-trust setting.

7

Verify session creation and the final redirect

Confirm that a successful callback returns the expected session cookie and redirects back to the same public origin. Check whether the browser stores the cookie or reports a policy rejection.

8

Remove temporary diagnostics

Delete verbose request inspection, revoke any credentials accidentally exposed during testing, and retain only sanitized operational logging appropriate for the application.

Interpret the final status code in context

A successful network delivery does not necessarily produce a successful OAuth result. A response from Next.js with an authentication error proves that the callback reached the application, which narrows the problem to route handling, provider exchange, temporary state, session creation, or origin validation. A connection failure indicates an earlier routing boundary. A provider-hosted redirect error indicates the request was rejected before the callback.

Similarly, a 404 can mean the callback path is incorrect, the deployed build lacks the route, a local reverse proxy removed a path prefix, or the tunnel targets another process. Compare the public callback path with the path received by the application before changing provider credentials.

Keep OAuth debugging safe

A development tunnel creates a real public entry point. Treat it as internet-accessible even if the hostname is difficult to guess. Expose only the service and routes needed for the test, retain application authentication, and stop or delete the tunnel when remote access is no longer required.

Use separate development OAuth credentials where the provider supports that operational model. Keep client secrets in the application’s approved secret-management mechanism, not in browser code or repository files. A public OAuth client may have different credential behavior from a confidential server-side client, so follow the provider’s current documentation rather than transferring assumptions between client types.

Redact query parameters from callback logs because authorization codes and state values can appear in them. Avoid recording full cookie headers. If temporary diagnostics must reveal host or protocol processing, log only the minimum fields and remove that code after the investigation.

🔑 Protect all credentials Keep provider secrets and device-specific Localtonet tokens out of source control, client-side bundles, screenshots, and support messages.
🎯 Expose the smallest target Point the tunnel to the intended application service rather than an unrelated administrative interface or an unnecessarily broad local gateway.
🧹 Remove debug instrumentation Sanitized temporary logging is useful for diagnosis, but authentication requests should not remain subject to verbose data capture.
⏹️ Stop access when finished A tunnel remains available only while its client is connected and the tunnel is running. Stop or delete it after the debugging session.
A tunnel does not replace application authorization

Keep the Next.js application’s normal authentication, authorization, origin validation, and least-privilege controls enabled. Do not weaken callback validation or disable state, nonce, PKCE, host checks, or cookie protections merely to make a test pass.

Frequently asked questions

Should the OAuth callback use the Localtonet HTTPS URL or localhost?

For a flow initiated and completed through the public tunnel, the registered callback should use the tunnel’s public HTTPS origin plus the exact callback path required by the application. The local IP and port remain the Localtonet tunnel target. Do not register that internal target merely because Next.js listens there.

Why does OAuth work on localhost but fail through the tunnel?

The public flow introduces a different scheme and hostname. Failures commonly occur when the provider still has a localhost callback, the application generates an internal URL, the external HTTPS protocol is not interpreted correctly, temporary cookies belong to localhost, or the tunnel cannot reach the container target. Check those boundaries in that order.

Which Auth.js environment variable should I set for the public URL?

Use the setting documented for the exact authentication package and version installed in your application. The supplied evidence does not establish one universal variable across Auth.js and related versions, so this guide intentionally does not guess a name. Confirm the installed version first, then verify its official base-URL and trusted-host guidance.

Why do I receive a state, nonce, or PKCE error after the provider redirects back?

First confirm that the temporary cookie created before authorization is present on the callback request. Starting at localhost and returning to the public hostname, using a stale hostname, applying an incompatible cookie path, or having the browser reject a cookie can break correlation. Also verify that the callback reaches the same intended application configuration.

Does the local Next.js server also need HTTPS?

The public browser endpoint and local tunnel target are separate connections. An HTTP tunnel can provide a public HTTPS address while pointing to a local HTTP service. The application must nevertheless generate and validate URLs and cookies according to the external HTTPS origin.

Why does the main page load while the OAuth callback returns 404?

The tunnel can be healthy while the callback path is wrong. Compare the path registered with the provider, the path sent in redirect_uri, and the route present in the running Next.js build. Also check whether a local reverse proxy changes the path or whether the tunnel points to a different application process.

What causes an untrusted-host error behind an HTTPS tunnel?

Authentication software may reject a host that it has not been configured to trust, or it may not accept proxy-derived host information by default. Confirm the public hostname, inspect the actual incoming request information, and follow the host-trust mechanism documented for the exact installed version. Do not disable host validation globally.

What address should Localtonet target when Next.js runs in Docker?

Use the IP address and port that can be reached from the environment running the Localtonet client. If the client runs on the Docker host, this is commonly an intentionally published host-side endpoint. If both services run in containers, use their deliberate container-network path. Verify reachability rather than assuming that loopback refers to the Next.js container.

Will the public callback continue working after I close the Localtonet client?

No. The tunnel is available only while the selected client device is connected and the tunnel is running. If the client stops or disconnects, the provider cannot deliver a new browser callback through that tunnel until connectivity is restored.

Test your Next.js OAuth callback with Localtonet

Verify the application locally, create an HTTP tunnel to the reachable Next.js target, and use the assigned public HTTPS origin consistently across the browser, provider registration, application configuration, and callback flow.

Get Started Free →

Localtonet is a secure multi-protocol tunneling and proxy platform designed to expose localhost, devices, private services, and AI agents to the public internet supporting HTTP/HTTPS tunnels, TCP/UDP forwarding, mobile proxy infrastructure, file server publishing, latency-optimized game connectivity, and developer-ready AI agent endpoint exposure from a single unified control plane.

support