You've done the part everyone tells you to check. You opened the Google credential in n8n, copied the OAuth Redirect URL field, pasted it into Authorized redirect URIs in the Google Cloud Console, saved, waited the few minutes Google says to wait for it to propagate, and tried again. Still the same wall: you click Sign in with Google, pick your account, and instead of connecting, you land on a plain Unauthorized — no detail, no field it's unhappy with, just that one word.
This is one of the more frustrating errors in n8n specifically because the advice you'll find everywhere — "make sure your redirect URI matches" — is correct, and you've already done it, and it's still broken. So this post is about the layer underneath that advice: on a self-hosted instance, the redirect URI n8n shows you isn't typed in by you, it's built automatically from how your n8n instance is configured to see itself. If that configuration is wrong, the URI you copied and the URI Google is actually being asked to redirect to are two different things, even though the field on your screen looks completely correct.
New to n8n or connecting your first Google account? This post assumes n8n is already running self-hosted (Docker or otherwise) and you're partway through connecting a Google credential. If you're setting up n8n itself first, start with using n8n for free with Docker.
What's actually happening
Every OAuth2 connection is built on a promise: the app (n8n) tells Google in advance exactly which URL it's going to send the user back to once they've approved access, Google checks that URL against a list it has on file, and if they don't match character for character, Google refuses the whole thing. On n8n Cloud, that URL is fixed and you never think about it. On self-hosted n8n, that URL is generated from your instance's own idea of its public address — and that idea comes from an environment variable, not from the browser tab you happen to be looking at it in.
So "Unauthorized" on self-hosted almost always traces back to one of two places: either the URI genuinely doesn't match on Google's side for a reason you haven't spotted yet, or n8n itself is generating the wrong URI in the first place, because it doesn't know its own real address.
The scenario: n8n running in Docker, mismatched between what you see and what n8n thinks
Picture n8n running in a Docker container, reachable at https://n8n.yourdomain.com through a reverse proxy. You open a Google credential, and the read-only OAuth Redirect URL field shows something — maybe https://n8n.yourdomain.com/rest/oauth2-credential/callback, which looks completely right, or maybe something you didn't expect at all, like a URL with localhost in it, or the wrong port. Everything below applies to either version of what that field actually shows you, because the fix in both cases is making sure n8n's idea of its address matches its real address.
Cause 1: the environment variable controlling your public address is wrong or missing
This is the one that's specific to self-hosting, and it's the one worth checking first, because it silently produces a redirect URI that looks plausible without being right. n8n builds its own public-facing URL from an environment variable — historically WEBHOOK_URL, though current n8n has moved to N8N_WEBHOOK_URL and treats the old name as deprecated — and if that variable isn't set at all, n8n falls back to constructing the URL from N8N_PROTOCOL, N8N_HOST, and N8N_PORT instead, which is exactly where it goes wrong behind a reverse proxy: n8n runs internally on its own port, the proxy exposes it to the world on a completely different one, and the auto-built URL can end up pointing at the internal address nobody outside your server can reach.
How to confirm it's this one: look at what the OAuth Redirect URL field actually shows, character by character, against what you'd type into a browser to reach your own n8n instance from outside. If it says localhost, an internal Docker hostname, an internal port like :5678 when your public site doesn't use that port, or http where your real site is https, that mismatch is the whole problem — and no amount of re-pasting the same wrong URL into Google's console will fix it, because you'd just be teaching Google to trust the wrong address too.
The fix: set your instance's public URL explicitly, matching your real, externally reachable address exactly — protocol, domain, and any path, with no trailing typos. Whether your version of n8n reads that from N8N_WEBHOOK_URL or the older WEBHOOK_URL depends on which release you're running; check your own environment configuration for whichever one is currently set, since having neither is what causes the fallback behavior above. Restart the n8n container after changing it — this is an environment variable, not a setting the UI can pick up live — and only then re-copy the OAuth Redirect URL field, because it will have changed.
Cause 2: the redirect URI matches, but the OAuth consent screen is still in Testing mode
If the redirect URI genuinely lines up on both sides — you've confirmed it character for character after fixing Cause 1, or it was already correct — the next most common source of a flat "Unauthorized" sits on Google's side of the fence: the OAuth consent screen for your project. An app left in Testing publishing status only allows sign-in from accounts you've explicitly added as test users, and an account that isn't on that list gets rejected before it ever gets near your redirect URI.
How to confirm it's this one: this is where we hand off to Google's own documentation rather than guess at console click-paths — the console flow itself (and whether it currently requires a payment method on file before you can even create a project, which has been true for some accounts recently) is worth confirming directly on Google's side rather than from a screenshot that might already be stale by the time you read this. Google's guide to configuring the OAuth consent screen covers publishing status, test users, and what "Testing" restricts, straight from the source.
The fix, in outline: either add the Google account you're testing with as a test user under the consent screen's audience settings, or move the app out of Testing once you're ready for real use — which has its own separate verification requirements Google documents in the same place. This is a Google Cloud Console setting, not an n8n one, so once it's fixed there's nothing further to change in your credential.
Cause 3: the redirect URI matches, but it's registered against the wrong OAuth client
It's easy to end up with more than one OAuth client in the same Google Cloud project — one from an earlier attempt, one you copied from a tutorial, one a teammate created — and paste your n8n redirect URI into the wrong one's Authorized redirect URIs list while pointing your Client ID and Client Secret at a different client entirely. The URI on your n8n credential is correct, the URI sitting in some client in Google's console is correct, they're just not the same client, and Google can't connect those dots for you.
How to confirm it's this one: in the Google Cloud Console credentials list, open the specific OAuth client whose Client ID matches the one you pasted into n8n — not just any client in the project — and check its Authorized redirect URIs list directly on that entry.
The fix: paste the redirect URI onto the same client whose Client ID and Client Secret you're using in n8n. If you've accumulated several test clients while troubleshooting, it's worth deleting the ones you're not using rather than leaving them to cause this exact confusion again later.
The part that breaks — the fix looks identical to the problem
Here's what makes this error genuinely hard to self-diagnose: a wrong N8N_WEBHOOK_URL and a correctly-set one that just hasn't been picked up yet look exactly the same in the UI. Changing the environment variable doesn't retroactively update anything — you have to restart the n8n process for it to reread its configuration, and if you edit the variable but don't restart, the credential modal keeps showing you the old, wrong redirect URL as if nothing changed, and you'll go back to Google's console convinced the URI you're pasting is the current one when it isn't.
What this looks like on your screen, concretely: you fix the environment variable, refresh the browser tab n8n is open in, open the credential, and the OAuth Redirect URL field shows the exact same value it showed before you made the change. That's not n8n ignoring your fix — it's n8n still running with the old configuration in memory. Restart the container (or the process, if you're not in Docker), then open the credential fresh, and the field should reflect the new address.
Where to take it
Restart n8n after any environment variable change touching its public address — the UI has no way to know you edited a variable outside itself, and it won't reread it until the process restarts.
Copy the redirect URI fresh, after the restart, rather than reusing one you copied earlier in the session — it's the one detail most likely to have silently changed underneath you.
Check the OAuth client ID in n8n against the OAuth client ID in Google's console side by side if you've created more than one client while troubleshooting — a mismatched pair is invisible until you compare the IDs directly.
For the console-side steps — creating the OAuth client, setting the consent screen's publishing status, adding test users — Google's own documentation is the accurate, current source: Google OAuth2 for a single service on n8n's side, and configuring the OAuth consent screen on Google's.
New to n8n credentials generally? The complete beginner's guide explains what a credential is and how nodes use it, plus the free 24-video course. OAuth is the hardest kind to start with, so the rest is easier than this.
Get the next one
If OAuth has cost you an afternoon on a self-hosted instance before, that's exactly the category of failure I keep digging into — the ones where every individual step looks right and the error message tells you almost nothing. Subscribe and the next one lands with the gotcha already flagged.
For the full map of every n8n guide on the site, in order, the n8n hub has all of them.
FAQ
Why does n8n say "Unauthorized" instead of a specific error when Google OAuth2 fails?
"Unauthorized" is a generic response Google's OAuth flow returns when it rejects the request for any of several reasons — a redirect URI mismatch, an account that isn't approved as a test user while the app is in Testing mode, or a client ID/secret pair that doesn't match the client the redirect URI was registered against. n8n surfaces whatever Google sends back, and Google doesn't distinguish between these cases in the message itself, which is why the fix requires ruling causes out one at a time rather than reading the error for a direct answer.
The redirect URI I copied from n8n looks correct — why would it still be wrong?
On self-hosted n8n, that field isn't something you typed — it's generated from your instance's configured public address, most commonly the N8N_WEBHOOK_URL environment variable (or the older WEBHOOK_URL, which n8n now treats as deprecated). If that variable is missing, wrong, or was changed without restarting n8n, the field can show a URL that looks plausible — a real domain, a real path — while still not matching your instance's actual, externally reachable address.
I changed my environment variable but the redirect URL field still shows the old value — is that a bug?
No — n8n reads that variable when the process starts, not live while it's running. Editing the variable in your .env file or Docker Compose config doesn't take effect until you restart the container or process. Restart it, then reopen the credential to see the updated value.
Do I need to verify my Google app before OAuth will work?
Not for testing with your own account — an app in Testing publishing status works fine as long as the Google account you're signing in with is listed as a test user on the OAuth consent screen. Verification becomes necessary once you want accounts outside that test-user list to be able to connect, which Google's own OAuth consent screen documentation covers in full, including what counts as sensitive scopes that trigger a stricter review.
Can I test this whole flow on localhost before deploying self-hosted n8n publicly?
Yes — Google accepts localhost as a valid redirect URI specifically for development, so you can connect a Google credential on a local n8n instance without a public domain, SSL certificate, or port forwarding, exactly as long as the redirect URI n8n shows you matches what's registered in the Google Cloud Console. The moment you move to a real domain, the redirect URI changes, and you'll need to register the new one in the same console entry.
Why does the redirect URI need to match exactly, down to the trailing slash?
OAuth2's redirect step is a security control, not a convenience feature — Google checks the URI you're asking it to send the authorization response to against an exact allow-list, precisely so a malicious app can't register a broad pattern and intercept another app's login. A trailing slash, http versus https, or a different subdomain all count as a different URI to that check, even when they'd resolve to the same place in a browser.


