Skip to main content

Overview

Password protection adds an additional security layer to your links. When a link is password-protected, users must provide the correct password via the X-Link-Password header to access the resource. Include the password parameter when creating a link:
Passwords are hashed using BCrypt before storage. The plain-text password is never stored on the server.
To access a password-protected link, include the X-Link-Password header in your request.

For URL Redirects

curl -L http://localhost:8080/l/abc123 \
  -H "X-Link-Password: MySecurePassword123"

For File Downloads

curl -L http://localhost:8080/l/xyz789 \
  -H "X-Link-Password: FilePass456" \
  -o downloaded-file.pdf

Getting JSON Response Instead of Redirect

For URL redirects, you can request JSON format by setting the Accept header:
curl http://localhost:8080/l/abc123 \
  -H "X-Link-Password: MySecurePassword123" \
  -H "Accept: application/json"
Response:
{
  "type": "REDIRECT",
  "targetUrl": "https://example.com/secure"
}

Password Validation Flow

The password authentication follows this sequence:
1

Request received

The server receives a request to /l/{shortCode} with the X-Link-Password header.
2

Link lookup

The server finds the link by short code and checks if it’s password-protected.
3

Password required check

If the link is password-protected but no password header is provided, a 401 Unauthorized response is returned.
4

Password validation

The provided password is compared against the stored BCrypt hash using PasswordEncoder.matches().
5

Access granted or denied

  • If the password matches: Access is granted and the resource is returned
  • If the password is incorrect: 401 Unauthorized is returned

Error Responses

Missing Password

When accessing a password-protected link without providing the password:
curl -L http://localhost:8080/l/abc123
Response (401 Unauthorized):
{
  "timestamp": "2026-03-04T14:30:00Z",
  "status": 401,
  "error": "Unauthorized",
  "message": "Password required",
  "path": "/l/abc123"
}

Invalid Password

When providing an incorrect password:
curl -L http://localhost:8080/l/abc123 \
  -H "X-Link-Password: WrongPassword"
Response (401 Unauthorized):
{
  "timestamp": "2026-03-04T14:30:00Z",
  "status": 401,
  "error": "Unauthorized",
  "message": "Invalid password",
  "path": "/l/abc123"
}
Password validation occurs before other access checks like expiration and view limits. This prevents attackers from using timing attacks to determine if a link exists.

Security Implementation Details

The password protection implementation (from ResolveLinkServiceImpl:87-94):
if (link.isPasswordProtected()) {
  if (password == null || password.isBlank()) {
    handleDenied(link.getShortCode(), AccessResult.PASSWORD_REQUIRED, 
      "password_required", HttpStatus.UNAUTHORIZED, "Password required", context);
  }
  if (!passwordEncoder.matches(password, link.getPasswordHash())) {
    handleDenied(link.getShortCode(), AccessResult.INVALID_PASSWORD, 
      "invalid_password", HttpStatus.UNAUTHORIZED, "Invalid password", context);
  }
}

Security Features

Passwords are hashed using BCrypt, a strong adaptive hash function designed for password storage. This prevents password exposure even if the database is compromised.
All password attempts are logged with the result:
  • PASSWORD_REQUIRED - No password provided
  • INVALID_PASSWORD - Wrong password
  • SUCCESS - Correct password
This helps detect brute-force attacks.
The X-Link-Password header should be transmitted over HTTPS to prevent password interception. Never send passwords over unencrypted HTTP connections in production.
Passwords are never included in the URL or query parameters, preventing exposure in browser history, server logs, and referrer headers.

Best Practices

Password Strength

curl -X POST http://localhost:8080/api/links \
  -H "Content-Type: application/json" \
  -d '{
    "targetUrl": "https://example.com/secure",
    "password": "xK9$mP2!qL5#nR8@"
  }'
Use strong passwords with a mix of uppercase, lowercase, numbers, and special characters. Consider generating passwords programmatically for maximum security.
  • Use HTTPS in production: Always transmit passwords over encrypted connections
  • Generate strong passwords: Use cryptographically secure random password generators
  • Communicate passwords securely: Send passwords to users through separate channels (e.g., email the link, SMS the password)
  • Combine with expiration: Use both password protection and expiration for maximum security
  • Monitor failed attempts: Set up alerts for excessive failed password attempts
  • Rotate passwords: For long-lived links, consider periodic password rotation
curl -X POST http://localhost:8080/api/links/upload \
  -F "[email protected]" \
  -F "password=xK9$mP2!qL5#nR8@" \
  -F "expiresAt=2026-03-31T23:59:59+00:00" \
  -F "maxViews=3"
This creates a download link that:
  • Requires a strong password
  • Expires on March 31, 2026
  • Can only be downloaded 3 times

Testing Password Protection

Here’s a complete example workflow:
1

Create a password-protected link

curl -X POST http://localhost:8080/api/links \
  -H "Content-Type: application/json" \
  -d '{
    "targetUrl": "https://example.com/test",
    "password": "TestPass123"
  }'
2

Test without password (should fail)

curl -v http://localhost:8080/l/abc123
Expected: 401 Unauthorized with “Password required”
3

Test with wrong password (should fail)

curl -v http://localhost:8080/l/abc123 \
  -H "X-Link-Password: WrongPassword"
Expected: 401 Unauthorized with “Invalid password”
4

Test with correct password (should succeed)

curl -L http://localhost:8080/l/abc123 \
  -H "X-Link-Password: TestPass123"
Expected: 302 redirect to https://example.com/test

Next Steps

Link Expiration

Combine passwords with expiration settings

Creating Links

Learn all link creation options

Uploading Files

Password-protect file downloads

Security Metrics

Monitor password attempt patterns

Build docs developers (and LLMs) love