Security checklist for deploying a bot, site or API

Before deploying, keep secrets out of code, use HTTPS, update dependencies, validate input, limit permissions and review logs. If a secret leaks, replace it immediately.

Updated 7 min read

Learning objectives

After reading this article you will be able to:

  • Review the security items that matter before a bot, site or API goes live
  • Apply security headers and input validation in an Express API
  • Check with simple commands whether a secret ended up in Git
On this page

Many incidents on small bots, sites and APIs come from basic failures: a token in the repository, an outdated dependency, an endpoint without authentication. You do not need to be a security specialist to close those doors. You just need to walk through a list before deploying and revisit it now and then.

This checklist covers what a solo developer can do in an afternoon. The examples use a recent Node.js version and Express 5, but the items apply to any language. If your question is only about the Discord token, read how to protect your bot token.

The checklist, in order:

  1. Secrets out of the code and out of Git.
  2. Up-to-date dependencies.
  3. HTTPS and security headers.
  4. Authentication, authorization and input validation.
  5. Least privilege for tokens, keys and database users.
  6. Logs without sensitive data and errors that leak no details.
  7. A plan for when a secret leaks.

These items cover much of the OWASP Top 10:2025, such as Broken Access Control (A01), Security Misconfiguration (A02), Software Supply Chain Failures (A03) and Authentication Failures (A07).

InternetNetworkHTTPS, security headers and WAF3Code and dependenciesauthentication, validation, npm audit24Permissionsleast privilege for tokens, keys, database5Secretsenvironment variables, never in Git17Logsno sensitivedata6
The numbers are the checklist items: each one guards a layer, and the logs record what passes through all of them.

How do you keep secrets out of your code?#

Bot tokens, database passwords, API keys and webhook secrets enter the application through environment variables, never written in the code. Access to the repository should not, by itself, grant access to production. If the concept is new, read what environment variables are.

For local development, use a .env file and add it to .gitignore before the first commit:

bash
echo ".env" >> .gitignore
git ls-files .env                # should print nothing
git log --all --oneline -- .env  # should print nothing

The first command confirms the file is not tracked. The second shows whether it was ever committed, on any branch. If a commit shows up, the secret is in the history: revoke it and issue a new one. The OWASP secrets guide is clear that rewriting Git history has side effects and does not replace rotation.

Three more habits:

  • Do not bake secrets into Docker images with ENV or ARG. Inject them at run time.
  • Use a different secret per environment (test and production).
  • Turn on GitHub secret scanning, which looks for exposed credentials across the full history on all branches and raises an alert. It runs automatically on public repositories.

How do you keep dependencies safe?#

Your code is small, but node_modules is not. Every package is third-party code running with your application's permissions, and new vulnerabilities appear without you changing anything.

bash
npm audit --omit=dev      # report on production dependencies only
npm audit fix             # applies compatible fixes

npm audit sends your dependency list to the registry and returns known vulnerabilities, exiting with a non-zero code if it finds any. Fixes that require changing a version range need --force, which can break your code, so run your tests afterward.

To avoid relying on memory, enable Dependabot security updates on GitHub: it opens pull requests with the minimum patched version when an alert is raised. Keep package-lock.json in the repository so the deploy installs the exact versions you tested, and install with npm ci.

Be wary of packages with names similar to popular ones and of brand-new packages with few downloads: that is how supply chain attacks get in.

How do you set up HTTPS and security headers?#

All traffic coming from outside should use HTTPS. Without it, passwords and tokens travel in plain text and any network on the path can read or alter the response. On a host with automatic certificates, you never issue one by hand. See how SSL/TLS works.

After HTTPS come the headers. The helmet package sets several of them at once, including Strict-Transport-Security, X-Content-Type-Options: nosniff, Content-Security-Policy and Referrer-Policy, and removes X-Powered-By, which reveals that you use Express.

bash
npm install express@5 helmet
js
import express from "express";
import helmet from "helmet";

const { API_TOKEN } = process.env;
if (!API_TOKEN) {
  console.error("API_TOKEN is missing");
  process.exit(1);
}

const app = express();
app.use(helmet());
app.use(express.json({ limit: "100kb" }));

// authentication before any private route
app.use((req, res, next) => {
  const token = (req.headers.authorization ?? "").replace(/^Bearer /, "");
  if (token !== API_TOKEN) return res.status(401).json({ error: "Unauthorized" });
  next();
});

app.post("/items", (req, res) => {
  const { name } = req.body ?? {};
  if (typeof name !== "string" || name.length === 0 || name.length > 100) {
    return res.status(400).json({ error: "name must be 1 to 100 characters" });
  }
  res.status(201).json({ name });
});

// generic error: the detail goes to the log, not to the client
app.use((err, _req, res, _next) => {
  console.error(err);
  res.status(500).json({ error: "Internal Server Error" });
});

app.listen(Number(process.env.PORT ?? 3000));

Strict-Transport-Security only takes effect on HTTPS responses: the browser remembers, for the number of seconds in max-age, that the site must only be reached over HTTPS. Helmet's default is one year with includeSubDomains. The default Content-Security-Policy is strict, so a site that loads scripts and images from other domains must adjust it. For an API that only returns JSON, it rarely gets in the way.

For the rest of the API (rate limiting, CORS, constant-time token comparison), follow the API production checklist.

How do you validate input and limit access?#

Treat everything from outside as hostile: body, query string, headers, uploaded file names and even the messages users send to your bot. Check type, length and format, and use parameterized queries instead of building SQL by concatenating strings.

Authenticating is not enough: Broken Access Control tops the OWASP Top 10:2025. If GET /orders/42 returns order 42 to any authenticated user, anyone can change the number and read other people's orders. Every route that touches a resource must check that the user owns it.

The same applies to bot commands: a command that deletes messages or changes settings must check the caller's role or permission instead of trusting that the command is hidden.

Least privilege. Each credential should be able to do only what its use requires. The OWASP secrets guide recommends giving each secret the minimum privileges and limiting CI/CD credentials to the secrets the job actually uses.

  • Bot: request only the permissions and intents it needs, never "Administrator" for convenience.
  • Database: an application user that cannot create or drop tables, separate from the migration user.
  • API keys: one key per service, so you can revoke one without breaking the others.
  • People: someone who only reads logs does not need access to deploy.

What should you log, and what should you never log?#

Log events that help an investigation: failed logins, 401 and 403 responses, 500 errors and administrative actions. Logging and alerting failures are their own category in the OWASP Top 10:2025, because without records you do not notice an attack.

Never log passwords, tokens, card numbers or the whole request body. And do not return a stack trace to the client: it reveals paths, libraries and versions. The example above logs the full error on the server and replies only "Internal Server Error".

What do you do when a secret leaks?#

Act in this order: revoke, replace, investigate.

  1. Revoke the secret at the source (Discord portal, database dashboard, API provider). Deleting the commit does not help.
  2. Generate a new one and update the environment variable on your host.
  3. Restart the application so it reads the new value.
  4. Investigate the service's logs for suspicious use during the window the secret was exposed.
  5. Fix the cause: .gitignore, secret scanning and a review of who had access.

How do you verify it worked, and which mistakes should you avoid?#

With the application live, check the headers and authentication:

bash
curl -sI https://your-app.example.com | grep -iE "strict-transport|x-content-type|x-powered-by"
curl -i https://your-app.example.com/items -X POST -H "Content-Type: application/json" -d '{"name":"a"}'

Expected: strict-transport-security and x-content-type-options present, x-powered-by absent and the second call returning 401. Also run npm audit --omit=dev and git log --all --oneline -- .env.

The most common mistakes:

  • Committing .env "just once": the history keeps it forever.
  • A bot token in a screenshot or error log pasted into a forum or support server.
  • CORS with * on an authenticated API.
  • An admin panel without authentication, assuming an unknown URL is enough.
  • Ignoring npm audit warnings for months.
  • The same database password in test and production.

How to do this on Square Cloud#

Square Cloud is self-service hosting: you deploy and define the configuration, so the checklist is still yours, but several items have something to lean on.

  1. Secrets (item 1): per-application environment variables, in the dashboard, the API, the CLI or VS Code. The dashboard imports your local .env, and a change applies on the next restart with no rebuild. For a GitHub Action deploy, the only credential is an API key stored as a repository secret.
  2. Dependencies (item 2): at start, when there is no node_modules, the platform runs npm install with NODE_ENV=production, so devDependencies never reach production and npm audit --omit=dev covers exactly what runs. Ship your package-lock.json so the install uses the versions you tested.
  3. HTTPS (item 3): every web application gets a *.squareweb.app subdomain with HTTPS. The certificate renews itself, HTTP redirects to HTTPS, and Helmet's headers, such as HSTS, pass through intact.
  4. Network and isolation: every plan includes a WAF and L3/L4/L7 DDoS protection, and applications run in isolated containers without root access; one cannot read another's variables or files, even in the same account. Those layers do not replace authentication, validation and rate limiting in your code.
  5. Leaks (item 7): change the variable and restart the application from the dashboard, the CLI or the API, watching it come back in the real-time logs.

After the deploy, run the two curl commands from the verification section against your squareweb.app subdomain. See the details in network and protection and environment variables.

Infrastructure

L3/L4/L7 DDoS mitigation and a WAF on every plan

Learn more

FAQs

What is the most common security mistake when deploying a bot or API?

Leaving a token, password or API key in the code or the repository. In a public repository anyone can copy it, so treat the secret as leaked and generate a new one.

Does deleting the .env file fix it if I already committed it?

No. The secret stays in the Git history. Revoke it and issue a new credential; cleaning the history helps, but it does not replace rotating the secret.

Do I need HTTPS for a bot that only talks to Discord or Telegram?

The bot itself only makes outbound connections, which already use TLS. HTTPS becomes mandatory when it receives traffic, such as a webhook, a web dashboard or an API.

How often should I run npm audit?

On every deploy and automatically on the repository, for example with Dependabot. New vulnerabilities are published constantly, even when you do not touch your code.

Next articleHow to protect your app from DDoS attacksTo protect against DDoS, combine edge mitigation (CDN and WAF) with rate limiting, timeouts and caching in your app. Step-by-step guide with working code.

More in Security

See all 10 articles