Every database driver accepts a URL, but no two databases agree on what that URL means. You copy a mysql:// string into a Postgres client and get a cryptic handshake error. You paste a Redis password containing @ and the driver silently connects to the wrong host. These are not edge cases; they are the normal experience of moving between stacks.
Here is the format each one actually expects, and the parts that break.
The general shape
All three follow the same skeleton:
scheme://[user[:password]@]host[:port][/database][?param=value] The differences live in the scheme name, the meaning of the path segment, how TLS is expressed, and which characters are legal in the password.
MySQL
mysql://app_user:S3cret@db.internal:3306/orders?ssl-mode=REQUIRED&charset=utf8mb4 - Path is the database name.
/ordersselects the schema on connect. - Default port is 3306.
- TLS is
ssl-mode, with valuesDISABLED,PREFERRED,REQUIRED,VERIFY_CA,VERIFY_IDENTITY.REQUIREDencrypts but does not verify the certificate, which is the setting people mistake for secure. -
charset=utf8mb4matters more than most guides admit. Without it, emoji and four-byte CJK characters are rejected or mangled, and the failure shows up at insert time, not connect time.
Node's mysql2 accepts this URL directly. Go's go-sql-driver/mysql does not; it wants user:pass@tcp(host:3306)/orders?parseTime=true and will reject the URL form.
PostgreSQL
postgresql://app_user:S3cret@db.internal:5432/orders?sslmode=require - Path is the database name, which in Postgres is a separate namespace from the schema. If you need a non-default schema, add
&options=-c%20search_path%3Dreporting. - Default port is 5432.
- TLS is
sslmode, withdisable,allow,prefer,require,verify-ca,verify-full. The dangerous default isprefer: it uses TLS when the server offers it and silently downgrades when it does not.verify-fullis the only value that checks the hostname. -
ssl=trueis a JDBC-ism. libpq-based clients ignore it.
Password special characters are the single most common Postgres URL failure. p@ss:word/1 must become p%40ss%3Aword%2F1. If you generate the string by hand, you will eventually ship a broken one.
Redis
redis://:S3cret@cache.internal:6379/2 rediss://default:S3cret@cache.internal:6380/0 Redis is where the conventions diverge hardest:
- No username slot in the old format. The user field is empty and the colon is still required:
redis://:password@host:6379. Drop the colon and the driver reads the password as the username, then fails authentication with a message that never mentions the missing colon. - ACL users (Redis 6+) do use the username slot:
redis://reporting:S3cret@host:6379/0. - Path is the database index, a number
0–15by default, not a name./2is database 2. Writing/cacheyields an error or silently database 0, depending on the client. -
rediss://(two s) is TLS. A singlesmeans plaintext, and nothing warns you.
The four things that break first
1. Unencoded special characters. Any of @ : / ? # [ ] % inside a password must be percent-encoded. # truncates the URL at the fragment. @ splits at the wrong place. This is not a client bug; the URL grammar simply does not allow them raw.
2. Empty passwords versus no password. user:@host and user@host are different. Some drivers send an empty credential and get "access denied"; others skip authentication entirely.
3. IPv6 hosts. mysql://user@::1:3306/db is ambiguous. Bracket the address: mysql://user@[::1]:3306/db.
4. Unix sockets. None of these URL forms express a socket path. MySQL clients accept ?socket=/var/run/mysqld/mysqld.sock, Postgres expects ?host=/var/run/postgresql, and Redis has no URL equivalent at all.
A way to stop hand-editing them
The parameters are short, the encoding rules are unforgiving, and the failure messages point at the wrong layer. Two habits save time:
- Generate the string once, then treat it as an opaque secret. Do not retype it into a second tool.
- Keep the TLS parameter explicit.
ssl-mode=REQUIREDandsslmode=requireare the values that make the connection behave the same in staging and production.
I keep browser-side generators bookmarked for this: MySQL connection string, PostgreSQL, and Redis. They percent-encode the password for you and show the result as a copyable string, which removes the whole class of "it worked in my terminal" bugs.
Summary
| MySQL | PostgreSQL | Redis | |
|---|---|---|---|
| Scheme | mysql:// | postgresql:// | redis:// / rediss:// |
| Default port | 3306 | 5432 | 6379 |
| Path segment | database | database | database index |
| TLS parameter | ssl-mode | sslmode | scheme (rediss) |
| Username optional | yes | yes | only with ACL |
Once you know where each one diverges, connection strings stop being mysterious and become a format you can write correctly the first time.


