---
title: "Secure HAProxy APIs with API Key Authentication"
description: "Secure your HAProxy API using a shared secret."
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/c/haproxy/secure-header"
framework: "HAProxy"
language: "C"
authStrategy: "shared secret header"
pageType: use-case
---

# Secure HAProxy APIs with API Key Authentication

Secure your HAProxy API using a shared secret.

## How Zuplo Handles It

Put Zuplo in front of your HAProxy backend to authenticate API keys and forward a shared secret header so your origin only accepts traffic from Zuplo.

## HAProxy Backend Code

```c
// Example HAProxy configuration with Lua script for shared secret validation

// First, ensure Lua is enabled in your HAProxy configuration
global
    lua-load /etc/haproxy/validate_secret.lua

frontend my_frontend
    bind *:80
    http-request lua.validate_secret

    # Define the backend to forward requests to if validated
    default_backend my_backend

backend my_backend
    server my_server localhost:8080

// Lua script: /etc/haproxy/validate_secret.lua
core.register_action("validate_secret", {"http-req"}, function(txn)
    -- Fetch the shared secret from environment variable
    local expected_secret = os.getenv("SHARED_SECRET")
    if not expected_secret then
        txn:set_var("txn.error_message", "Server configuration error")
        txn:done()
        return txn:send_response(500, "Internal Server Error", txn:get_var("txn.error_message"))
    end

    -- Get the secret from the HTTP header
    local secret = txn.req:get_header("x-shared-secret")
    if not secret then
        txn:set_var("txn.error_message", "No secret provided")
        txn:done()
        return txn:send_response(401, "Unauthorized", txn:get_var("txn.error_message"))
    end

    -- Perform a timing-safe comparison
    if #secret ~= #expected_secret then
        txn:set_var("txn.error_message", "Invalid secret")
        txn:done()
        return txn:send_response(401, "Unauthorized", txn:get_var("txn.error_message"))
    end

    local matched = 0
    for i = 1, #secret do
        if secret:byte(i) ~= expected_secret:byte(i) then
            matched = 1
        end
    end

    if matched == 1 then
        txn:set_var("txn.error_message", "Invalid secret")
        txn:done()
        return txn:send_response(401, "Unauthorized", txn:get_var("txn.error_message"))
    end

    -- If no error, proceed to the backend
    txn:done()
end)
```

## Example Request

```bash
curl -X GET \
  'https://your-api.zuplo.dev/your-route' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

## Learn More

- [API Key Authentication on Zuplo](https://zuplo.com/docs/policies/api-key-auth-inbound)
- [JWT Authentication on Zuplo](https://zuplo.com/docs/policies/open-id-jwt-auth-inbound)
- [All use cases](https://zuplo.com/use-cases)
