vpsFocus
Русский Sign in Create account

AI built your site. Here are the holes it left on your server

AI assistants write code that works. That's exactly the danger: since everything works, nobody looks at what else is open. The assistant solves “make it work”, not “make it safe”, and takes the shortest path: open a port, allow everyone, leave debug mode on.

Below are eight mistakes that show up most often in sites built with Cursor, Claude Code, Lovable, Bolt and similar tools and deployed to a VPS. Each one takes a minute to check. At the end there's a ready-made prompt to have the same AI review your project.

1. .env is served publicly or committed to git

.env holds the database password and API keys. It can leak in two ways.

The site serves it at an address. This happens when the web server serves the whole project folder. Check from your own computer:

curl -s -o /dev/null -w "%{http_code}\n" https://your-site.com/.env
curl -s -o /dev/null -w "%{http_code}\n" https://your-site.com/.git/config

200 — the file is open to everyone. 404 or 403 — good.

It ended up in the repository. If the repository is public, the key has already been found: bots scan GitHub for fresh keys within minutes of a commit.

git log --all --oneline -- .env

If this prints anything, the file is in the history. Deleting the file doesn't help: it stays in old commits. Change every key and password in it first, and only then clean the history.

2. A secret key in the frontend

The most common mistake with OpenAI, Anthropic and Stripe keys. The assistant puts the API call right into the page code — it's quicker. But everything that runs in the browser is visible to every visitor.

Variables prefixed with NEXT_PUBLIC_, VITE_ or REACT_APP_ are especially sneaky: anything with such a prefix ends up in the code the browser receives.

To check, build the project and search the build output for keys:

grep -rE "sk-[A-Za-z0-9_-]{20,}|sk_live_" dist/ build/ .next/static/ 2>/dev/null

Or open the site, press F12, go to the Sources tab, search all files with Ctrl+Shift+F and look for sk-.

Found one — the key is compromised. Revoke it, issue a new one and move the API call to the server.

3. An open endpoint that spends your money

A new problem of the AI-app era. The server has an address like /api/chat that forwards a question to OpenAI or Claude. The key is hidden properly, but the endpoint itself is open to everyone — no login, no rate limit. Someone finds it and uses your key for free, and the bill comes to you.

Check from a computer where you're not logged in to the site:

curl -s -X POST https://your-site.com/api/chat \
  -H "Content-Type: application/json" -d '{"message":"hi"}'

If a model answer comes back, the endpoint is open. You need at least two things: a check that the request comes from a logged-in user, and a limit on the number of requests. Also set a spending limit in your AI provider's dashboard.

4. The database is open to the internet

The assistant writes docker-compose.yml with ports: "5432:5432" because it's easier to connect during development. On a server this means the database listens to the whole internet. Often with the password postgres or password from the example, too.

sudo ss -tlnp | grep -E "5432|3306|6379|27017"

0.0.0.0:5432 — the database is open. If an enabled ufw reassures you, it shouldn't: a port published by Docker bypasses ufw. docker-compose.yml should say 127.0.0.1:5432:5432, or better, have no ports at all — the site and the database on the same Docker network see each other anyway.

If your database is in Supabase or Firebase (as with many Lovable and Bolt projects), check something else: whether access rules are on (Row Level Security in Supabase). Without them, the public key from the frontend opens the whole database for reading and writing.

5. Development mode on the production server

A very common sight: on the VPS the site runs with npm run dev under pm2 or screen, because that's how it started working. A development server isn't meant for the internet: it's slower, shows detailed errors with paths and code, and sometimes gives access to project files.

ps aux | grep -E "next dev|vite|nodemon|flask run|manage.py runserver" | grep -v grep

Empty — good. If something shows up, build the project (npm run build) and run it in production mode (npm start or whatever your framework needs).

Separately, debug flags: DEBUG=True in Django, APP_DEBUG=true in Laravel, debug=True in Flask. On an error they show the visitor your settings and environment variables.

6. CORS that allows everyone

When the frontend can't reach the API because of CORS, the assistant often sets Access-Control-Allow-Origin: * or allows any origin. The error goes away, and so does the protection: any site on the internet can call your API from a visitor's browser.

curl -s -I -H "Origin: https://evil.example" https://your-site.com/api/ | grep -i "access-control"

If the answer has Access-Control-Allow-Origin: https://evil.example, or * together with Access-Control-Allow-Credentials: true, everyone is allowed. The list of allowed origins should contain only your own domain.

7. Open admin panels

phpMyAdmin, Adminer, pgAdmin, a Strapi or Directus panel, a framework's /admin — the assistant installs them “for convenience” and leaves them on a public address, sometimes with the default password.

for p in admin phpmyadmin adminer pgadmin; do
  printf "%-12s %s\n" "$p" "$(curl -s -o /dev/null -w '%{http_code}' https://your-site.com/$p/)"
done

Anything that answered 200 and shouldn't be public — close it: access only through an SSH tunnel, by an address list, or at least behind a separate password.

8. The server is set up “however it turned out”

AI writes code but doesn't configure the server. On the VPS itself you often find: root login with a password, no firewall, no security updates, the site running as root. That's a separate topic, covered in “Your site is live on a VPS. What to check next”.

Ask the AI to check its own code

The assistant that made these mistakes is good at finding them if you ask directly. Paste this into Cursor, Claude Code or another assistant:

Review this project for security issues before deploying it to a server.
Check specifically:
1. Whether .env or other secrets end up in git or in the frontend build
   (NEXT_PUBLIC_, VITE_, REACT_APP_ variables).
2. Whether paid APIs (OpenAI, Anthropic, Stripe) are called from code
   that runs in the browser.
3. Whether there are API endpoints without a login check and without
   rate limiting, especially ones that call paid APIs.
4. Whether database and service ports are exposed in docker-compose.yml.
5. Whether development mode, debug flags or default passwords are left on.
6. Whether CORS allows any origin.
For each finding: the file and line, why it's dangerous, how to fix it.
Don't change anything until I confirm.

The last line matters: otherwise the assistant may start “fixing” right away and break what worked.

How vpsFocus does this

vpsFocus is a free desktop app that looks at your server from the side and finds what's visible from outside and on the VPS itself:

The app doesn't read your project's code: check keys in the frontend and open API endpoints yourself — with the commands above or by asking your assistant.

Download vpsFocus