ComfyUI Node

HTTP Serving

Expose your ComfyUI workflow as a REST endpoint

By matan1905·Created 3 years ago·Updated about a year ago· 71
HTTP Serving
    • Serving config
    port8000
    enable_cross_origin_requestsfalse
    html_content <!DOCTYPE html> <html> <body> <input type="text" id="prompt" value=""> <button onclick="sendPost()">Send</button> <img id="resultImage" style="display:none"> <script> function sendPost() { const prompt = document.getElementById('prompt').value; fetch(window.location.href, { method: 'POST', body: JSON.stringify({ prompt }) }) .then(response => response.json()) .then(data => { document.getElementById('resultImage').src = 'data:image/png;base64,' + data.base64_img; document.getElementById('resultImage').style.display = 'block'; }); } </script> </body> </html>

    No Discord, no Telegram - HTTPServing is the node you reach for when you want to hit your ComfyUI workflow with plain old HTTP POST requests. It spins up a small web server on your machine and exposes your graph as an endpoint: you POST a JSON body like {"prompt": "a cat"}, the workflow runs, and you get JSON back containing a base64 image. It's the cleanest way to bolt a ComfyUI workflow onto a web page, a script, or another service without standing up any infrastructure in between.

    Under the hood it's Python's standard http.server running in a thread. The node listens on a port (default 8000), accepts POSTs, parses the JSON, and routes the request through the same command system as the rest of the pack. Here's the part people miss: the URL path is the command. POST /generate only works if a CommandPickerServing node registered generate as a command - otherwise you get a 404 with {"error": "Command not found"}. So unlike Discord or Telegram, HTTP serving doesn't work until you add a command gate. Add an AlwaysExecute node if you want every path to be a free-for-all.

    The inputs that matter:

    • port - where the server listens. Default 8000; pick something above 1024 so you don't need root.
    • enable_cross_origin_requests - off by default. Flip it on if a web page served from a different origin needs to call you (it adds Access-Control-Allow-Origin: * to responses). Leave it off otherwise.
    • html_content - the page served at GET /. The default is a bare text box and a Send button that POSTs the prompt and shows the returned image. It's just editable HTML, and the author's own suggestion is to get an LLM to extend it with inputs for your extra arguments. This is the nice part of HTTP serving: no bot, just a page that talks to your workflow.

    The response JSON always carries a _requestId alongside whichever output fired: base64_img from ServingOutput, base64_images from ServingMultiImageOutput, and/or text from ServingTextOutput. A quick smoke test from a terminal:

    curl -X POST http://localhost:8000/generate \
      -H "Content-Type: application/json" \
      -d '{"prompt": "a lighthouse at dusk"}'
    

    Install via ComfyUI Manager ("Serving Toolkit") or the standard clone + pip install -r requirements.txt into custom_nodes. There are no model downloads - the server just serves your existing workflow.

    Now the warning, and it's a real one: this server has no authentication. Anybody who can reach the port can run your workflow, and ComfyUI's ecosystem is already notorious for unauthenticated API exposure being a security hole. Keep the port bound to localhost or a trusted network - don't port-forward it, don't put it on a public box. If you need remote access, put it behind something that authenticates. Also remember the pack-wide rule: the node blocks waiting for a request, so Auto Queue must be on or the server never gets a chance to process anything after the first run.

    CategoryServing-Toolkit

    Inputs (3)

    NameTypeDefaultDescription
    portINT80001–65535
    enable_cross_origin_requestsBOOLEANfalse
    html_contentSTRING <!DOCTYPE html> <html> <body> <input type="text" id="prompt" value=""> <button onclick="sendPost()">Send</button> <img id="resultImage" style="display:none"> <script> function sendPost() { const prompt = document.getElementById('prompt').value; fetch(window.location.href, { method: 'POST', body: JSON.stringify({ prompt }) }) .then(response => response.json()) .then(data => { document.getElementById('resultImage').src = 'data:image/png;base64,' + data.base64_img; document.getElementById('resultImage').style.display = 'block'; }); } </script> </body> </html>

    Outputs (1)

    NameTypeDescription
    Serving configSERVING_CONFIG