Skip to content

# Exposing FastCGI Servers

> FastCGI is a binary protocol for interfacing interactive programs with a web server. [...] (It's) aim is to reduce the overhead related to interfacing between web server and CGI programs, allowing a server to handle more web page requests per unit of time.

> — Wikipedia

The ingress-nginx ingress controller can be used to directly expose FastCGI servers. Enabling FastCGI in your Ingress only requires setting the backend-protocol annotation to `FCGI`, and with a couple more annotations you can customize the way ingress-nginx handles the communication with your FastCGI server.

For most practical use-cases, php applications are a good example. PHP is not HTML so a FastCGI server like php-fpm processes a index.php script for the response to a request. See a working example below.

This post in a FactCGI feature issue describes a test for the FastCGI feature. The same test is described below here.

## Example Objects to expose a FastCGI server pod

### The FasctCGI server pod

The Pod object example below exposes port `9000`, which is the conventional FastCGI port.

    apiVersion: v1
    kind: Pod
    metadata:
      name: example-app
      labels:
        app: example-app
    spec:
      containers:
      - name: example-app
        image: php:fpm-alpine
        ports:
        - containerPort: 9000
          name: fastcgi

- For this example to work, a HTML response should be received from the FastCGI server being exposed
- A HTTP request to the FastCGI server pod should be sent
- The response should be generated by a php script as that is what we are demonstrating here

The image we are using here `php:fpm-alpine` does not ship with a ready to use php script inside it. So we need to provide the image with a simple php-script for this example to work.

- Use `kubectl exec` to get into the example-app pod
- You will land at the path `/var/www/html`
- Create a simple php script there at the path /var/www/html called index.php
- Make the index.php file look like this

    <!DOCTYPE html>
    <html>
        <head>
            <title>PHP Test</title>
        </head>
        <body>
            <?php echo '<p>FastCGI Test Worked!</p>'; ?>
        </body>
    </html>

- Save and exit from the shell in the pod
- If you delete the pod, then you will have to recreate the file as this method is not persistent

### The FastCGI service

The Service object example below matches port `9000` from the Pod object above.

    apiVersion: v1
    kind: Service
    metadata:
      name: example-service
    spec:
      selector:
        app: example-app
      ports:
      - port: 9000
        targetPort: 9000
        name: fastcgi

### The configMap object and the ingress object

The Ingress and ConfigMap objects below demonstrate the supported FastCGI specific annotations.

Important

NGINX actually has 50 FastCGI directives All of the nginx directives have not been exposed in the ingress yet

### The ConfigMap object

This configMap object is required to set the parameters of FastCGI directives

Attention

- The ConfigMap must be created before creating the ingress object

- The Ingress Controller needs to find the configMap when the Ingress object with the FastCGI annotations is created
- So create the configMap before the ingress
- If the configMap is created after the ingress is created, then you will need to restart the Ingress Controller pods.

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: example-cm
    data:
      SCRIPT_FILENAME: "/var/www/html/index.php"

### The ingress object

- Do not create the ingress shown below until you have created the configMap seen above.
- You can see that this ingress matches the service `example-service`, and the port named `fastcgi` from above.

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      annotations:
        nginx.ingress.kubernetes.io/backend-protocol: "FCGI"
        nginx.ingress.kubernetes.io/fastcgi-index: "index.php"
        nginx.ingress.kubernetes.io/fastcgi-params-configmap: "example-cm"
      name: example-app
    spec:
      ingressClassName: nginx
      rules:
      - host: app.example.com
        http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: example-service
                port:
                  name: fastcgi

## Send a request to the exposed FastCGI server

You will have to look at the external-ip of the ingress or you have to send the HTTP request to the ClusterIP address of the ingress-nginx controller pod.

    % curl 172.19.0.2 -H "Host: app.example.com" -vik
    *   Trying 172.19.0.2:80...
    * Connected to 172.19.0.2 (172.19.0.2) port 80
    > GET / HTTP/1.1
    > Host: app.example.com
    > User-Agent: curl/8.6.0
    > Accept: */*
    >
    < HTTP/1.1 200 OK
    HTTP/1.1 200 OK
    < Date: Wed, 12 Jun 2024 07:11:59 GMT
    Date: Wed, 12 Jun 2024 07:11:59 GMT
    < Content-Type: text/html; charset=UTF-8
    Content-Type: text/html; charset=UTF-8
    < Transfer-Encoding: chunked
    Transfer-Encoding: chunked
    < Connection: keep-alive
    Connection: keep-alive
    < X-Powered-By: PHP/8.3.8
    X-Powered-By: PHP/8.3.8

    <
    <!DOCTYPE html>
    <html>
        <head>
            <title>PHP Test</title>
        </head>
        <body>
            <p>FastCGI Test Worked</p>    </body>
    </html>

## FastCGI Ingress Annotations

To enable FastCGI, the `nginx.ingress.kubernetes.io/backend-protocol` annotation needs to be set to `FCGI`, which overrides the default `HTTP` value.

> `nginx.ingress.kubernetes.io/backend-protocol: "FCGI"`

This enables the FastCGI mode for all paths defined in the Ingress object

### The `nginx.ingress.kubernetes.io/fastcgi-index` Annotation

To specify an index file, the `fastcgi-index` annotation value can optionally be set. In the example below, the value is set to `index.php`. This annotation corresponds to the NGINX `fastcgi_index` directive.

> `nginx.ingress.kubernetes.io/fastcgi-index: "index.php"`

### The `nginx.ingress.kubernetes.io/fastcgi-params-configmap` Annotation

To specify NGINX `fastcgi_param` directives, the `fastcgi-params-configmap` annotation is used, which in turn must lead to a ConfigMap object containing the NGINX `fastcgi_param` directives as key/values.

> `nginx.ingress.kubernetes.io/fastcgi-params-configmap: "example-configmap"`

And the ConfigMap object to specify the `SCRIPT_FILENAME` and `HTTP_PROXY` NGINX's `fastcgi_param` directives will look like the following:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: example-configmap
    data:
      SCRIPT_FILENAME: "/example/index.php"
      HTTP_PROXY: ""

Using the namespace/ prefix is also supported, for example:

> `nginx.ingress.kubernetes.io/fastcgi-params-configmap: "example-namespace/example-configmap"`