Skip to main content

Query String Forwarding: forward parameters to sub-requests

Query String Forwarding adds selected query string parameters to nested file addresses in a playlist before Orange's CDN returns the file. It is mainly used for HLS and MPEG-DASH video, where a viewer requests a master manifest first, and that manifest references rendition manifests, media segments, subtitles, or other related files.

A viewer requests a protected HLS master manifest with a secure token:

http://demo-files-protected.gvideo.io/coffee_run/master.m3u8?expires=1893456000&md5=eBx15p01_a9JNuo1iZpTfQ

Without this option, URLs for nested manifests and segments in the playlist stay unchanged, so they have no query string parameters:

manifest.m3u8?expires=1893456000&md5=eBx15p01_a9JNuo1iZpTfQ
index720.m3u8                 <= as is, without token parameters
chunk720-1.ts                 <= as is, without token parameters

When Query String Forwarding is enabled, the CDN inserts the md5 and expires parameters from the initial request into nested manifests and segments:

manifest.m3u8?expires=1893456000&md5=eBx15p01_a9JNuo1iZpTfQ
index720.m3u8?expires=1893456000&md5=eBx15p01_a9JNuo1iZpTfQ
chunk720-1.ts?expires=1893456000&md5=eBx15p01_a9JNuo1iZpTfQ
Query string parameters from the master manifest request are added to nested playlist links

Configure in the Customer Portal

Set Query String Forwarding on a CDN resource in the CDB Technical Web Portal.

  1. 1

    Open the CDN resource

    Navigate to CDN and select the CDN resource to configure.

  2. 2

    Open Query String Forwarding

    In the sidebar, under the Content section, click Query String Forwarding.

    CDN Query String Forwarding settings
  3. 3

    Enable forwarding

    Enable the option.

  4. 4

    Set the file types and keys

    Apply these settings for HLS and DASH video:

    • Forward from file types: add m3u8, mpd, or any others.
    • Forward to file types: add ts, mp4, m3u8, m4s, vtt, or any others.
    • Forward only keys: add the query string parameter names to copy: md5, expires, or any others.
  5. 5

    Save the resource

    Save the CDN resource.

Use these links to check how Query String Forwarding works with a real ABR stream:

Links to demo files are valid until January 1, 2030.

HLS and MPEG-DASH playlists keep a secure token on md5 and expires.

A player requests one playlist, and that playlist names the renditions and segments without the query string from the first request. Query string forwarding copies selected parameters onto those nested addresses. Orange's CDN rewrites the links inside the playlist before it returns the file.

An API token is required. The CDN resource ID is the id from the resources list call.

export GCORE_API_KEY="{YOUR_API_KEY}"
export CDN_RESOURCE_ID="{YOUR_CDN_RESOURCE_ID}"

Forward selected parameters

Copy md5 and expires from requests for m3u8 and mpd files onto the nested ts, mp4, m3u8, m4s, and vtt files.

enabled, forward_from_file_types, and forward_to_file_types are required. Each list needs at least one value, with no repeats, and each extension is written without a leading dot. forward_only_keys and forward_except_keys cannot be sent in the same request.

import os

from gcore import CDB

client = CDB()
resource_id = int(os.environ["CDN_RESOURCE_ID"])

updated = client.cdn.cdn_resources.update(
    resource_id,
    options={
        "query_string_forwarding": &#123;
            "enabled": True,
            "forward_from_file_types": ["m3u8", "mpd"],
            "forward_to_file_types": ["ts", "mp4", "m3u8", "m4s", "vtt"],
            "forward_only_keys": ["md5", "expires"],
        &#125;
    },
)
saved = updated.options.query_string_forwarding
print(saved.enabled, saved.forward_from_file_types, saved.forward_only_keys)
package main

import (
    "context"
    "fmt"
    "os"
    "strconv"

    gcore "github.com/G-Core/gcore-go"
    "github.com/G-Core/gcore-go/cdn"
)

func main() {
    client := gcore.NewClient()
    ctx := context.Background()
    resourceID, err := strconv.ParseInt(os.Getenv("CDN_RESOURCE_ID"), 10, 64)
    if err != nil {
        panic(err)
    }

    updated, err := client.CDN.CDNResources.Update(ctx, resourceID, cdn.CDNResourceUpdateParams{
        Options: cdn.CDNResourceUpdateParamsOptions{
            QueryStringForwarding: cdn.CDNResourceUpdateParamsOptionsQueryStringForwarding{
                Enabled:              true,
                ForwardFromFileTypes: []string{"m3u8", "mpd"},
                ForwardToFileTypes:   []string{"ts", "mp4", "m3u8", "m4s", "vtt"},
                ForwardOnlyKeys:      []string{"md5", "expires"},
            },
        },
    })
    if err != nil {
        panic(err)
    }
    fmt.Println(updated.Options.QueryStringForwarding.Enabled, updated.Options.QueryStringForwarding.ForwardOnlyKeys)
}
curl -X PATCH "https://api.cdb-staging.cdn.orange.com/cdn/resources/$CDN_RESOURCE_ID" \
  -H "Authorization: APIKey $GCORE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "options": {
      "query_string_forwarding": &#123;
        "enabled": true,
        "forward_from_file_types": ["m3u8", "mpd"],
        "forward_to_file_types": ["ts", "mp4", "m3u8", "m4s", "vtt"],
        "forward_only_keys": ["md5", "expires"]
      &#125;
    }
  }'

The API returns HTTP 200. The saved option is:

&#123;
  "enabled": true,
  "forward_from_file_types": ["m3u8", "mpd"],
  "forward_to_file_types": ["ts", "mp4", "m3u8", "m4s", "vtt"],
  "forward_only_keys": ["md5", "expires"]
&#125;

Turn forwarding off

Set enabled to false and send the same file types and keys. Orange's CDN keeps the saved lists and stops copying the parameters. enabled alone returns HTTP 400, because the file-type lists are still required.

import os

from gcore import CDB

client = CDB()
resource_id = int(os.environ["CDN_RESOURCE_ID"])

updated = client.cdn.cdn_resources.update(
    resource_id,
    options={
        "query_string_forwarding": &#123;
            "enabled": False,
            "forward_from_file_types": ["m3u8", "mpd"],
            "forward_to_file_types": ["ts", "mp4", "m3u8", "m4s", "vtt"],
            "forward_only_keys": ["md5", "expires"],
        &#125;
    },
)
saved = updated.options.query_string_forwarding
print(saved.enabled, saved.forward_only_keys)
package main

import (
    "context"
    "fmt"
    "os"
    "strconv"

    gcore "github.com/G-Core/gcore-go"
    "github.com/G-Core/gcore-go/cdn"
)

func main() {
    client := gcore.NewClient()
    ctx := context.Background()
    resourceID, err := strconv.ParseInt(os.Getenv("CDN_RESOURCE_ID"), 10, 64)
    if err != nil {
        panic(err)
    }

    updated, err := client.CDN.CDNResources.Update(ctx, resourceID, cdn.CDNResourceUpdateParams{
        Options: cdn.CDNResourceUpdateParamsOptions{
            QueryStringForwarding: cdn.CDNResourceUpdateParamsOptionsQueryStringForwarding{
                Enabled:              false,
                ForwardFromFileTypes: []string{"m3u8", "mpd"},
                ForwardToFileTypes:   []string{"ts", "mp4", "m3u8", "m4s", "vtt"},
                ForwardOnlyKeys:      []string{"md5", "expires"},
            },
        },
    })
    if err != nil {
        panic(err)
    }
    fmt.Println(updated.Options.QueryStringForwarding.Enabled, updated.Options.QueryStringForwarding.ForwardOnlyKeys)
}
curl -X PATCH "https://api.cdb-staging.cdn.orange.com/cdn/resources/$CDN_RESOURCE_ID" \
  -H "Authorization: APIKey $GCORE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "options": {
      "query_string_forwarding": &#123;
        "enabled": false,
        "forward_from_file_types": ["m3u8", "mpd"],
        "forward_to_file_types": ["ts", "mp4", "m3u8", "m4s", "vtt"],
        "forward_only_keys": ["md5", "expires"]
      &#125;
    }
  }'

The API returns HTTP 200. The saved option is:

&#123;
  "enabled": false,
  "forward_from_file_types": ["m3u8", "mpd"],
  "forward_to_file_types": ["ts", "mp4", "m3u8", "m4s", "vtt"],
  "forward_only_keys": ["md5", "expires"]
&#125;

Remove forwarding

Send query_string_forwarding as JSON null to remove the option. The next read returns null.

import os

from gcore import CDB

client = CDB()
resource_id = int(os.environ["CDN_RESOURCE_ID"])

updated = client.cdn.cdn_resources.update(
    resource_id,
    options=&#123;"query_string_forwarding": None&#125;,
)
print(updated.options.query_string_forwarding)
package main

import (
    "context"
    "fmt"
    "os"
    "strconv"

    gcore "github.com/G-Core/gcore-go"
    "github.com/G-Core/gcore-go/cdn"
)

func main() {
    client := gcore.NewClient()
    ctx := context.Background()
    resourceID, err := strconv.ParseInt(os.Getenv("CDN_RESOURCE_ID"), 10, 64)
    if err != nil {
        panic(err)
    }

    options := cdn.CDNResourceUpdateParamsOptions{}
    options.SetExtraFields(map[string]any&#123;
        "query_string_forwarding": nil,
    &#125;)
    updated, err := client.CDN.CDNResources.Update(ctx, resourceID, cdn.CDNResourceUpdateParams{
        Options: options,
    })
    if err != nil {
        panic(err)
    }
    fmt.Println(updated.Options.QueryStringForwarding.RawJSON())
}
curl -X PATCH "https://api.cdb-staging.cdn.orange.com/cdn/resources/$CDN_RESOURCE_ID" \
  -H "Authorization: APIKey $GCORE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "options": &#123;
      "query_string_forwarding": null
    &#125;
  }'

The API returns HTTP 200. The saved option is:

null

The Terraform provider v2 adds md5 and expires from a playlist request to the nested file addresses before Orange's CDN returns the playlist. A master manifest requested with those parameters is returned with the same parameters on the rendition and segment links. Run terraform apply after editing options.

Forward selected parameters

Orange's CDN rewrites links inside m3u8 and mpd files so nested ts, mp4, m3u8, m4s, and vtt files receive md5 and expires from the parent request. Set enabled to true. forward_from_file_types and forward_to_file_types are required. Write each extension without a leading dot.

forward_only_keys lists the parameters to copy. forward_except_keys lists parameters to leave out. Sending both returns HTTP 400.

resource "gcore_cdn_origin_group" "example" {
  name = "my-origin-group"

  sources = [
    {
      source  = "example.com"
      enabled = true
    }
  ]
}

resource "gcore_cdn_resource" "example" {
  cname           = "cdn.example.com"
  origin_group    = gcore_cdn_origin_group.example.id
  origin_protocol = "HTTPS"

  options = {
    query_string_forwarding = {
      enabled                  = true
      forward_from_file_types = ["m3u8", "mpd"]
      forward_to_file_types   = ["ts", "mp4", "m3u8", "m4s", "vtt"]
      forward_only_keys       = ["md5", "expires"]
    }
  }
}

Turn forwarding off

Set enabled to false and keep the same file types and keys. Orange's CDN keeps the saved lists and stops copying the parameters. A resource created without this block leaves query string forwarding unset.

resource "gcore_cdn_origin_group" "example" {
  name = "my-origin-group"

  sources = [
    {
      source  = "example.com"
      enabled = true
    }
  ]
}

resource "gcore_cdn_resource" "example" {
  cname           = "cdn.example.com"
  origin_group    = gcore_cdn_origin_group.example.id
  origin_protocol = "HTTPS"

  options = {
    query_string_forwarding = {
      enabled                  = false
      forward_from_file_types = ["m3u8", "mpd"]
      forward_to_file_types   = ["ts", "mp4", "m3u8", "m4s", "vtt"]
      forward_only_keys       = ["md5", "expires"]
    }
  }
}