Skip to content

Http Server

Artem Podorozhko edited this page Jul 21, 2026 · 1 revision

Http Server

CefContentHttpServer is the WebUserInterface plugin's small Unreal HTTP bridge for serving UTexture2D assets to a browser or another local UI client. It starts a route at GET /img, resolves an Unreal asset path, loads and caches the texture, encodes its first mip as PNG, and returns the image bytes.

Source: Source/CefContentHttpServer

Why use it

Use this module when HTML or CEF content needs to display Unreal-owned images by asset path, for example:

  • inventory, character, or item thumbnails;
  • map and level previews;
  • settings or profile images stored as Unreal textures;
  • dynamic UI images that should not be copied into a separate web project;
  • a simple local asset endpoint alongside the CEF browser session.

The module keeps the browser-facing request small: the client sends an Unreal asset path and receives a normal PNG response. It also caches loaded textures, caches encoded PNG bytes, and deduplicates concurrent requests for the same asset.

Use CefContentHttpServer for asset delivery and simple request/response needs. Use CefWebSocketServer for live bidirectional messages, commands, and state updates. This endpoint does not provide authentication, authorization, or a general-purpose file server; add an access-control layer before exposing it to untrusted clients.

Default behavior

The UCefContentHttpServerSubsystem is a UGameInstanceSubsystem. During subsystem initialization it:

  1. selects UCefContentDefaultImageRequestHandler when no custom handler is configured;
  2. starts the Unreal HTTP server on port 18080;
  3. binds the GET /img route.

The default success response is:

HTTP 200
Content-Type: image/png

The route returns JSON error bodies for failed requests, with a status such as 400, 404, or 500.

Quick start

Request a texture by Unreal package path:

http://localhost:18080/img?asset=/Game/Folder/T_Image

For an asset stored at:

Content/Folder/T_Image.uasset

send:

/Game/Folder/T_Image

The cache normalizes that package path to the object path /Game/Folder/T_Image.T_Image internally. Supplying the full object path is also supported.

Example browser usage:

<img src="http://localhost:18080/img?asset=/Game/UI/T_InventoryIcon" alt="Inventory icon">

Example JavaScript request:

const response = await fetch(
  "http://localhost:18080/img?asset=/Game/UI/T_InventoryIcon"
);

if (!response.ok) {
  throw new Error(await response.text());
}

const imageBlob = await response.blob();
const imageUrl = URL.createObjectURL(imageBlob);
document.querySelector("#icon").src = imageUrl;

Accepted request formats

The server reads the asset path in this order:

  1. query parameter asset;
  2. query parameter aasset (supported as a compatibility alias);
  3. JSON body with an asset field;
  4. JSON body with an asset= field;
  5. raw UTF-8 body beginning with asset=.

Examples:

GET /img?asset=/Game/UI/T_Icon
{"asset":"/Game/UI/T_Icon"}
asset=/Game/UI/T_Icon

The query-string form is recommended for <img> tags and browser caching. The JSON or raw-body forms are useful when a client already builds request payloads programmatically.

Blueprint API

Get the subsystem with Get Cef Content Http Server Subsystem from a world-context object, or use the UCefContentHttpServerBPLibrary helper.

Useful functions include:

Function Purpose
Start Server Starts the listener. Pass a positive port to override the configured port; pass 0 to keep the configured port.
Stop Server Stops the listener and unbinds /img.
Is Server Running Checks whether the listener and route are active.
Get Image By Package Path Loads an image through the module cache for game-side use.
Get Cached Image Count Reports the loaded texture cache size.
Clear Cached Images Clears loaded texture and encoded PNG caches.
Set Request Handler Class Replaces the /img handler with a Blueprint or C++ subclass.

The subsystem starts automatically, but calling Start Server is useful after an explicit stop or when a port override is needed.

Port and lifecycle

The default port is declared on the subsystem as HttpPort = 18080. A positive argument to StartServer becomes the new listening port; 0 keeps the current configured port. Starting an already-running server is safe and leaves the current listener in place.

The subsystem stops the route during Deinitialize. It also stops before replacing a handler when SetRequestHandlerClass is called with bInRestartIfRunning = true (the default).

If the port is occupied, startup fails rather than silently selecting another port. Choose a free port and ensure the browser uses the same value.

Custom request handlers

The default handler is appropriate for PNG texture delivery. Create a subclass of UCefContentHttpImageRequestHandler when the endpoint needs custom lookup, authorization, formats, or response data.

Each handler receives FCefContentHttpImageRequestContext:

  • AssetPath - resolved asset value;
  • RawBody - UTF-8 request body;
  • QueryParams - parsed query values.

It fills FCefContentHttpImageResponse:

  • StatusCode;
  • ContentType;
  • Body as raw response bytes.

Minimal C++ example:

#include "Handlers/CefContentHttpImageRequestHandler.h"

UCLASS()
class UMyUiImageHandler : public UCefContentHttpImageRequestHandler
{
    GENERATED_BODY()

public:
    virtual bool HandleImageRequest_Implementation(
        const FCefContentHttpImageRequestContext& request,
        FCefContentHttpImageResponse& response,
        FString& error) override
    {
        if (request.AssetPath.IsEmpty())
        {
            response.StatusCode = 400;
            response.ContentType = TEXT("application/json");
            error = TEXT("Missing asset path");
            return false;
        }

        // Resolve and validate the request, then fill response.Body.
        response.StatusCode = 200;
        response.ContentType = TEXT("application/octet-stream");
        return true;
    }
};

Assign the handler from game code:

if (UCefContentHttpServerSubsystem* subsystem =
        GetGameInstance()->GetSubsystem<UCefContentHttpServerSubsystem>())
{
    subsystem->SetRequestHandlerClass(UMyUiImageHandler::StaticClass(), true);
}

The handler also supports HandleImageRequestAsync. Use it when loading, encoding, or authorization work should complete asynchronously. The built-in handler uses this path to load on the game thread, encode PNG data on a worker, cache encoded bytes, and join concurrent requests for the same asset.

Caching and performance

There are two related caches:

  1. Texture cache - loaded UTexture2D objects keyed by normalized asset path.
  2. Encoded cache - PNG byte arrays keyed by the requested asset path.

The first request may load and encode the asset. Later requests can reuse cached data. Concurrent requests for an asset already being encoded join the same in-flight operation instead of encoding it repeatedly.

Call ClearCachedImages when assets have changed or memory should be released. This clears both the loaded texture cache and encoded PNG cache. Monitor cache size with GetCachedImageCount and the module's Unreal stats when diagnosing memory use.

The default encoder reads the first texture mip and expects usable platform data with four-channel pixel data. For unusual texture formats, virtual textures, or non-UTexture2D assets, use a custom handler or encoder strategy.

Error responses

Status Typical cause
400 The asset value is missing.
404 The asset cannot be loaded or is not found.
500 The module, handler, texture data, or PNG encoder is unavailable or failed.

Errors are returned as JSON, for example:

{"error":"Missing required 'asset' parameter"}

Troubleshooting

The browser cannot connect

Check that IsServerRunning is true, port 18080 is free, and the URL uses the actual configured port. Look for route-bind or listener errors in the Unreal log.

The response is 404

Use the Unreal object/package path, not a Windows filesystem path. Confirm the asset is a UTexture2D and that the asset is available in the running build.

The response is 500 or the image is empty

Check that the texture has platform data and a valid first mip. Clear the caches after replacing or reimporting the asset, then retry.

A custom handler is not being called

Call SetRequestHandlerClass with the correct handler class and keep bInRestartIfRunning enabled, or stop and start the server manually after changing the handler.