Writing
Database
Mingtindu Sherpa6 min read

How to URL-Encode Special Characters in PostgreSQL Connection Strings

Encode PostgreSQL usernames and passwords safely without breaking the scheme, host, port, database name, or query parameters.

On this page

If a PostgreSQL password contains @, #, %, /, or a space, place that value in the URL only after percent-encoding the username or password component. Do not encode the complete connection URL: doing so would also encode structural characters such as :, /, @, and ? that the parser needs.

Use the PostgreSQL Connection URL Builder when you want to assemble and inspect the format field by field in your browser. Use placeholder credentials on shared devices.

PostgreSQL URL anatomy

A typical URL has this structure:

postgresql://USER:PASSWORD@HOST:PORT/DATABASE?sslmode=require

The punctuation separates structural parts:

  • postgresql:// is the scheme.
  • USER:PASSWORD is the user information.
  • @ ends the user information and begins the network location.
  • HOST:PORT identifies the endpoint.
  • /DATABASE selects the database.
  • ?name=value&name=value carries connection parameters.

Those separators are URL structure. The username and password are URL components. A literal @ inside a password is data, but an unencoded @ is also meaningful syntax. Percent-encoding removes that ambiguity.

For a broader explanation of every field, read PostgreSQL Connection URL Explained, Piece by Piece.

Common encoded values

Percent-encoding represents a byte as % followed by two hexadecimal digits.

CharacterEncoded value
@%40
#%23
%%25
/%2F
Space%20

A literal percent sign must become %25. If the original password contains %40, encoding the original value produces %2540; otherwise a parser could treat %40 as an encoded @ rather than as the four literal characters %, 4, and 0.

Encode each dynamic component in JavaScript

JavaScript's encodeURIComponent is appropriate for an individual username or password:

const username = encodeURIComponent("app_user");
const password = encodeURIComponent("demo@pass#50%/with space");
 
const databaseUrl =
  `postgresql://${username}:${password}@db.example.com:5432/app`;

The result has encoded credentials while preserving the URL grammar:

postgresql://app_user:demo%40pass%2350%25%2Fwith%20space@db.example.com:5432/app

Apply the same rule to any other dynamic URL component. For example, encode a dynamic database name separately before interpolating it. Treat the host and port as validated configuration rather than accepting arbitrary text and trying to repair it with encoding.

Do not encode the entire URL

This is incorrect:

const broken = encodeURIComponent(
  "postgresql://app_user:demo@pass@db.example.com:5432/app",
);

It begins with postgresql%3A%2F%2F, so it no longer has a recognizable postgresql:// scheme. Encoding must happen before the components are joined.

Do not encode an already encoded password twice

encodeURIComponent("demo%40pass") returns demo%2540pass. That is correct only if the user's literal password contains the characters %40. If %40 was already intended to represent @, a second encoding changes the credential.

Keep the unencoded secret in a secret manager, encode it once when constructing a URL, and avoid round-tripping between encoded and decoded copies.

Space, plus, and form encoding are different contexts

encodeURIComponent("with space") produces with%20space. That is the unambiguous representation to use for a space in a PostgreSQL connection URI component.

Some web form encodings use + for a space. A URI parser does not universally apply that form rule, and PostgreSQL clients or intermediary URL libraries can differ in how they handle a literal plus. If the credential contains +, component encoding produces %2B; if it contains a space, it produces %20. Do not substitute + for %20 by hand.

Prisma DATABASE_URL example

Prisma requires special characters in PostgreSQL connection URL components to be percent-encoded. A safe placeholder value is:

DATABASE_URL="postgresql://app_user:demo%40pass%2350%25%2Fwith%20space@db.example.com:5432/example_database?schema=public&sslmode=require"

The quotes above belong to dotenv syntax; they are not part of the URL value. Provider dashboards may supply an already encoded string, so inspect their documentation before transforming it again.

Errors malformed URLs can cause

Encoding failures do not always produce an explicit "bad encoding" message.

  • An unencoded @ can make the parser read part of the password as the host.
  • An unencoded / can prematurely begin the path containing the database name.
  • An unencoded # can be treated as a fragment marker by a general URL parser, truncating the effective value.
  • A stray % or invalid percent escape can cause parsing to fail.
  • Double encoding can make authentication fail because the decoded password no longer matches the original.

These can surface as an invalid connection string, failed authentication, an unknown host, or a reachability error. Diagnose the parsed structure before assuming PostgreSQL itself is unavailable.

Verify the format without revealing credentials

Do not print the full URL. A small Node.js check can report structure and mask user information:

const parsed = new URL(process.env.DATABASE_URL ?? "");
 
console.log({
  protocol: parsed.protocol,
  hasUsername: parsed.username.length > 0,
  hasPassword: parsed.password.length > 0,
  hostname: parsed.hostname,
  port: parsed.port || "default",
  databasePathPresent: parsed.pathname.length > 1,
  sslmode: parsed.searchParams.get("sslmode"),
});

Only run this where the hostname and database path are not themselves sensitive. It verifies the shape, not the correctness of the credential or the reachability of the server.

Keep connection strings out of history and logs

A connection URL is a credential. Do not place a real one directly in a shell command: shell history, process listings, CI output, application logs, and copied terminal transcripts can preserve it. Keep it in an approved secret store or environment variable, never source control.

If a real URL is exposed, rotate the database credential. Merely deleting a log line or later Git commit does not invalidate the leaked secret.

Encoding fixes only the URL grammar. It does not choose a trust policy or connection endpoint. Continue with PostgreSQL SSL Modes Explained for Node.js and Prisma, then compare a direct PostgreSQL connection with client-side and external pools.

Checklist

  • Encode individual dynamic components, not the complete URL.
  • Encode a literal % as %25 and a literal + as %2B.
  • Represent a space as %20, not an assumed form-style +.
  • Avoid encoding a provider-supplied value twice.
  • Validate URL structure without printing credentials.
  • Keep the finished URL out of shell history, logs, and source control.

References

Documentation checked on 2026-08-08:

Related writing

Share