A small web app with a landing page that shows NordVPN's recommended WireGuard
server — country flag, city, endpoint IP, hostname, live load meter and feature
tags — and builds a ready-to-import WireGuard .conf from it. Deploys to
Netlify or Vercel with no changes.
🔒 Your private key never leaves your browser. The config is assembled client-side. The serverless functions proxy the public server lookup — and, only if you opt into it, one authenticated call that turns your NordVPN access token into a private key (details).
The page auto-loads a recommended server on open and displays:
- Flag + country + city (flags are SVGs from flagcdn — emoji flags don't render on Windows)
- Endpoint IP, hostname, and server name
- A load meter (green / amber / red by utilization)
- Feature chips from the server's groups (e.g. P2P)
- A country picker to choose any location, plus Get another server to re-roll, and a link to view the location on a map
NordVPN's recommendations endpoint picks the closest server based on the IP
that calls it. If the serverless function called it directly, every visitor
would get a server near the deploy region (e.g. a US server from a US
datacenter). So the browser detects the visitor's country client-side (via
geojs.io, falling back to ipapi.co / ipwho.is), maps it to a NordVPN country id
using /api/countries, and passes it to /api/recommend?country=<id>. The
picker lets visitors override the detected country.
Below that, two more sections: fetch your private key from a NordVPN access
token, then the config builder — private key → live .conf preview → download
or copy.
Both steps are addressable, so you can link straight to the one you need:
| Link | Lands on |
|---|---|
https://nordconf-wg.netlify.app/#private_key |
the access-token → private-key step |
https://nordconf-wg.netlify.app/#build_conf |
the .conf builder |
Aliases are accepted and case-insensitive: #private-key, #privatekey, #key,
#token all resolve to the first; #build-conf, #buildconf, #build,
#config, #conf to the second. An unrecognised hash is ignored and the page
loads normally.
The target section is scrolled to the top, flashed briefly so it's obvious where you landed, and its input is focused — except on touch devices, where the on-screen keyboard would cover it. Because the hero grows when the server loads (pushing the sections below it down), the position is corrected once after the first render rather than relying on the browser's initial jump alone.
If you don't have your NordLynx private key handy, paste a NordVPN access token instead (Nord Account → Services → NordVPN → Manual setup → Generate new token). The app exchanges it for the key and fills the builder below.
⚠️ The token does pass through the serverless function. NordVPN's/v1/users/services/credentialsendpoint requires HTTP Basic auth and sends no CORS headers, so the browser can't call it directly. The function forwards the token once, never logs or stores it, and respondsCache-Control: no-store. This is the one exception to the "nothing sensitive leaves the browser" rule — the private key itself still never does.
Don't want to trust the proxy? The page has a Run it yourself instead toggle with the equivalent command:
curl -s -u token:<YOUR_TOKEN> \
https://api.nordvpn.com/v1/users/services/credentials \
| grep -o '"nordlynx_private_key":"[^"]*"'Browser ──click──▶ /api/recommend ──▶ NordVPN recommendations API
▲ │
│ name, endpoint IP, │
└─── public key ◀─────┘
│
└─ you type your PrivateKey ─▶ .conf is built & downloaded locally
The serverless function exists only to dodge browser CORS on the NordVPN API. It never receives — and never handles — your private key.
| Config field | Source |
|---|---|
name (file name) |
API → name (e.g. Greece #111 → saved as Greece_111.conf) |
Endpoint IP |
API → station; port 51820 is fixed for wireguard_udp |
PublicKey (peer) |
API → the wireguard_udp technology's metadata.public_key |
PrivateKey |
You — typed into the browser, kept local; or fetched via /api/credentials from your access token |
Address = 10.5.0.2/16 |
Static NordLynx client address (same for every server) |
DNS = 103.86.96.100 |
Static NordVPN resolver (secondary 103.86.99.100) |
Get your PrivateKey from your NordVPN account's manual WireGuard setup page, or use the access-token section described above.
public/ # static front-end (served at /)
index.html
style.css
app.js # builds & downloads the .conf, entirely client-side
lib/nordvpn.js # shared: recommendation, country list, token → key
api/ # Vercel serverless functions
recommend.js
countries.js
credentials.js # POST { token } → { privateKey }
netlify/functions/ # same three, Netlify flavour
recommend.js
countries.js
credentials.js
vercel.json # Vercel: static = public/, function config
netlify.toml # Netlify: publish dir, functions dir, /api redirects
Both platforms serve public/ as the site and expose the functions under
/api/*.
- Push this folder to a Git repo (GitHub/GitLab/Bitbucket).
- In Vercel, New Project → Import the repo.
- Framework preset: Other. Leave build command empty. Deploy.
Or from the CLI:
npx vercel # preview
npx vercel --prod # production- Push to a Git repo.
- In Netlify, Add new site → Import an existing project, pick the repo.
netlify.tomlsets everything (publish dir, functions,/apiredirect).
Or from the CLI:
npx netlify-cli deploy # draft
npx netlify-cli deploy --prod # productionThe static page alone (the Fetch button needs the function, so use a platform CLI for the full flow):
npm run dev # static only, http://localhost:3000
npm run dev:vercel # full app with the function (Vercel CLI)
npm run dev:netlify # full app with the function (Netlify CLI)Node 18+ is required (the function uses the built-in global fetch).
- The generated
.confcontains your private key — treat it like a password.*.confis git-ignored. /api/recommendand/api/countriessend no query data beyond an optional numeric country id and never log or store request bodies./api/credentialsis the only endpoint that sees a secret. Your access token is forwarded to NordVPN in one Basic-auth request and discarded — not logged, not stored, response markedno-store. Treat the token like a password, and revoke it in Nord Account if you ever suspect it leaked. If you'd rather not send it at all, use thecurlcommand above and paste the key manually.
MIT