Resolving S7-1200 AWP Web Server XML Write Failures to PLC
Reading PLC tags from an S7-1200 CPU through its built-in Web server using XML fragments is straightforward, but writing data back to the controller through the same XMLHttpRequest-based mechanism is one of the most common failure points in AWP (Automation Web Programming) implementations. This reference isolates the root causes, documents the underlying protocol requirements, and supplies verified JavaScript and HTML patterns that successfully push data into the CPU's data blocks.
Problem Description
Engineers and integrators frequently implement a client-side JavaScript routine that performs an XMLHttpRequest against a URL of the form:
http://10.0.1.26/awp/hb/DBW.xml
The GET returns a perfectly valid XML payload, and getElementsByTagName("dato_<variable>") extracts the current value from the CPU. When the script attempts to mutate the node's nodeValue and re-issue the request, the variable in the CPU never updates. Symptoms reported across multiple deployments include:
- No CPU fault, diagnostic buffer entry, or LED change.
- The browser receives the original XML unmodified on every subsequent call.
- Network analyzers show the request returning
200 OKbut no write transaction on the S7 communication layer. - Browser DevTools shows no payload in the request body, even when
send()is called with arguments.
Root Cause Analysis
The AWP fragment interface is not a generic XML-RPC or REST endpoint. It is a thin HTTP wrapper around the CPU's process image that obeys strict semantics:
-
HTTP method dispatch. Reads are served on
GET; writes are served exclusively onPOSTwith the form encodingapplication/x-www-form-urlencoded. APUT,PATCH, or body-bearingGETis silently dropped by the Web server and produces no error. -
Fragment immutability. The fragment returned by
GET /awp/<name>.xmlis a server-rendered snapshot. Mutating the client-side DOM and re-fetching the URL does not propagate to the CPU because AWP does not interpret the request body on a read. -
Tag scope. Variables are only writable when they are declared with
AWP_In_Variablein the fragment definition and reside in a DB whose optimized block access is disabled, or are explicitly mapped through standard access. -
Authentication. When the Web server is configured with "Permit access with HTTPS and password" or user-level rights, write operations are rejected with
403 Forbiddenif no valid session cookie or Basic Auth header is present. - CPU protection level. A write requires the configured user to hold "Write access" in the CPU protection configuration. A user with read-only access produces a silent rejection.
The JavaScript function below is a typical failure pattern. It opens a synchronous GET, parses the response XML, mutates the node locally, and discards the change because no POST is issued:
function setdata_03(variable, valor) {
var xmlHttp = GetXmlHttpObject();
if (xmlHttp == null) { alert("Browser does not support AJAX"); return; }
var url = "http://10.0.1.26/awp/hb/DBW.xml";
var xmlDoc = loadXMLDoc(url);
var xmlNode = xmlDoc.getElementsByTagName("dato_" + variable)[0].childNodes[0];
xmlNode.nodeValue = valor; // local-only mutation
// No POST issued -> CPU never sees the value
}
Fixing the failure requires re-issuing the request as a POST with URL-encoded parameters, not mutating a previously fetched XML document.
S7-1200 Web Server AWP Fundamentals
The S7-1200 Web server is activated in the CPU's Device Configuration. Once enabled, the controller listens on TCP/80 (HTTP) or TCP/443 (HTTPS) and serves two classes of content: the Siemens standard diagnostic pages and user-defined fragments stored in the project under "Web server > User-defined pages."
For procedure and checkbox verification, see the official S7-1200 manual collection — Enabling the Web server entry in the TIA Portal cloud documentation. The enabling path is:
- Open the CPU in the Device Configuration view.
- Select Web server in the Inspector window properties.
- Check Activate Web server on this CPU.
- Select Permit access only via HTTPS for production deployments.
- Add a user under Users with the role that includes write access.
- Compile and download the hardware configuration.
AWP fragments are HTML files stored in the project tree under the "Web server" node. The CPU serves them at runtime from the load memory. The fragment URL convention is:
http://<CPU-IP>/awp/<FragmentName>.<ext>?[<var>=<value>&...]
For reading, the CPU renders the fragment as XML or HTML, substituting every :=<variable>: token with the live value. For writing, the CPU inspects the URL query parameters (POST body or GET query string) and copies matching values into the symbolic variable name declared with AWP_In_Variable.
Fragment Definition and AWP Directives
A writable fragment requires the following AWP directives in its source HTML/XML. The fragment below, named DBW.xml, exposes a single integer tag from DB10:
<?xml version="1.0" encoding="utf-8"?>
<!-- AWP_In_Variable Name='"DBW".SpeedSetpoint' -->
<!-- AWP_Start_Fragment Name="DBW" ID="10" Type="exclude" -->
<Data>
<dato_SpeedSetpoint>:="DBW".SpeedSetpoint:</dato_SpeedSetpoint>
</Data>
<!-- AWP_End_Fragment -->
Key rules from the Siemens AWP specification:
| Directive | Position | Purpose |
|---|---|---|
AWP_In_Variable Name='"DBx".VarName' |
Header | Declares a tag writable via HTTP POST/GET parameter. |
AWP_Out_Variable Name='"DBx".VarName' |
Header | Declares a tag readable; required for substitute output if not in fragment start. |
AWP_Start_Fragment Name="X" ID="n" |
Top | Begins a named, addressable fragment region. |
AWP_End_Fragment |
Bottom | Closes the fragment region. |
:="DBx".VarName: |
Body | Output substitution token; emits the current value. |
AWP_Enum_Def Name="X" Values='"0","1"' |
Header | Maps numeric values to text labels. |
The fully qualified symbolic name is composed of the data block name (in quotes) and the tag name, separated by a dot. The data block must use standard (non-optimized) access, or the CPU will reject the symbolic reference. To switch off optimized access in TIA Portal, right-click the DB in the project tree, choose Properties, and uncheck Optimized block access.
HTTP Method and Content-Type Requirements
The S7-1200 Web server accepts writes through two equivalent transports:
-
URL-encoded body:
POST /awp/DBW.xml HTTP/1.1withContent-Type: application/x-www-form-urlencodedand a body such as%22DBW%22.SpeedSetpoint=1450. The double quotes around the DB name must be URL-encoded as%22. -
Query string:
POST /awp/DBW.xml?%22DBW%22.SpeedSetpoint=1450 HTTP/1.1with no body. This is useful when the client cannot easily set request bodies (e.g., some embedded browsers).
The HTTP response from the CPU on a successful write is a short redirect (302 Found) to the same fragment URL without parameters, returning the refreshed XML. Many client libraries (including older XMLHttpRequest wrappers) treat 302 as an error. The request must allow the redirect, or the script must issue a follow-up GET to retrieve the post-write value.
%22DBW%22.SpeedSetpoint is the URL-encoding of "DBW".SpeedSetpoint. Using single quotes or omitting the DB name produces a silent write failure with no diagnostic entry.
Correct JavaScript Implementation
The script below rewrites the original setdata_03 pattern to issue a proper POST. It assumes the same DB10/SpeedSetpoint tag declared in the fragment above.
function setdata_03(tag, value) {
var xhr = new XMLHttpRequest();
// Step 1: encode the symbolic name exactly as Siemens expects
var symbol = encodeURIComponent('"DBW".') + encodeURIComponent(tag);
var body = symbol + '=' + encodeURIComponent(value);
// Step 2: open the request as POST, synchronous for simplicity
xhr.open('POST', '/awp/DBW.xml', false);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// Step 3: send the URL-encoded body
xhr.send(body);
// Step 4: optional follow-up GET to confirm the new value
if (xhr.status === 302 || xhr.status === 200) {
var verify = new XMLHttpRequest();
verify.open('GET', '/awp/DBW.xml', false);
verify.send();
return verify.responseXML;
}
return null;
}
Notes on the implementation:
-
xhr.open('POST', ...)is mandatory; the original code'sGETcannot transport a write. - The body must be a single key=value pair per variable. The CPU processes up to 32 variables per request; for larger payloads, fragment the writes.
- If the CPU is configured for HTTPS only, change the URL to
https://<IP>/awp/...and accept the self-signed certificate on the client. Modern browsers reject mixed content if the host page is loaded over HTTPS but the POST targets HTTP.
Authentication and CPU Protection
Writes are rejected when the Web server user lacks write rights. Configure users in TIA Portal under CPU properties > Web server > User management. The minimum required permission is the right "Write to PLC" granted through one of the three default roles (Standard user, Diagnostics, Operator) or a custom role.
For HTTP Basic authentication from JavaScript, the Web server does not natively support RFC 2617 Basic Auth on user-defined fragments without HTTPS session establishment. The recommended pattern is:
- Load
/FormLoginfirst to obtain the session cookie. - Pass the cookie back on every subsequent
POSTthrough theCookieheader.
For an unprotected network (machine-internal only), the CPU's protection level under CPU properties > Protection & Security can be set to Full access (no protection). This is not recommended for any production environment exposed to a corporate network.
Alternative: HTML Form-Based Write
When a JavaScript-free implementation is acceptable, the same fragment accepts a standard HTML form POST without any client scripting:
<form method="POST" action="/awp/DBW.xml">
<input type="text" name='"DBW".SpeedSetpoint' value="1450" />
<input type="submit" value="Write" />
</form>
The form submits a application/x-www-form-urlencoded body that the CPU parses identically to the XMLHttpRequest POST. This pattern is often used for engineering panel displays and is the recommended verification step because it isolates client-side code from AWP configuration issues.
Diagnostics: Confirming the Write Reached the CPU
After a write is issued, verify it reached the CPU using one or more of the following techniques:
-
Follow-up GET. Issue a GET to
/awp/DBW.xmland inspect the rendered XML. The post-write value should appear. - Watch table. Add the tag to a watch table in TIA Portal and force a re-read. The CPU updates its process image on each Web server request.
- Diagnostic buffer. Open Online & Diagnostics > Diagnostic buffer on the CPU. Security rejections generate entry W#16#007F with a "Web server" identifier; absent entries mean the request never reached the security layer.
-
Wireshark. Capture TCP/443 (or TCP/80) on the engineering station. A successful write request carries the symbolic name and value in the HTTP body. A failed write returns
403 Forbidden,401 Unauthorized, or302 Foundfollowed by an empty 200.
Troubleshooting Matrix
| Symptom | Probable Cause | Corrective Action |
|---|---|---|
| XMLHttpRequest returns 200 but CPU value unchanged | GET used instead of POST | Switch to xhr.open('POST', url, false) with URL-encoded body |
| Browser DevTools shows request body empty | Old send() called without argument |
Pass body string: xhr.send('key=value')
|
| 403 Forbidden returned | User lacks write rights, or Web server requires login | Adjust user role in TIA Portal; load /FormLogin first |
| Write accepted but value rejected by CPU | Symbolic name does not match DB tag | Verify DB is non-optimized and tag spelling is exact |
| Mixed content warning in console | Page served over HTTPS, POST sent over HTTP | Use HTTPS endpoint exclusively; install self-signed cert |
| CORS error in browser console | Page served from different origin than CPU | Host HTML on CPU, or use a reverse proxy with CORS headers |
| CPU reports "Unknown fragment" | Fragment not compiled into project | Recompile and download hardware; verify file in Web server tree |
| Intermittent writes during high traffic | Web server session timeout | Re-authenticate before each write, or extend session timeout in CPU properties |
Firmware and TIA Portal Compatibility Notes
AWP behavior is stable across firmware V4.0 through V5.0 (S7-1200 G2), but the following firmware-specific items affect writes:
- V4.0: Original AWP implementation. Supports GET/POST writes with 16-character tag name limit.
- V4.2: Extended to 32 characters. Adds HTTPS redirect enforcement.
-
V4.4: Tightens CSRF behavior. Writes from a different origin require
Refererheader match. - V4.5+: Adds simultaneous connection limit (default 4). Writes in tight loops can exhaust the pool.
- V5.0 (G2): Default TLS 1.3. Older client stacks that negotiate TLS 1.0 will fail to negotiate HTTPS but can still write to HTTP if not blocked.
The accompanying TIA Portal project should be at V17 or later to deploy user-defined Web pages reliably. V13 SP1 and earlier emit slightly different AWP_Start_Fragment attributes that the V4.5+ firmware interprets correctly but that older firmwares reject. If you must support legacy firmware, keep the TIA Portal version within one major release of the target firmware.
Best-Practice Checklist
- Always issue writes via
POSTwithapplication/x-www-form-urlencodedcontent type. - URL-encode the double quotes around the data block name (
%22). - Disable Optimized block access on any DB used in AWP.
- Assign a dedicated user with the lowest-privilege role that still permits writes.
- Use HTTPS for any deployment outside a trusted machine subnet.
- Place HTML on the CPU itself to avoid CORS preflight failures.
- Validate tag value ranges in PLC logic before they reach actuators; AWP writes bypass HMI tag limits.
- Limit write rate to one request per 100 ms to avoid saturating the Web server connection pool.
Verification Procedure
To confirm a complete working AWP write path after applying the corrections above:
- Open the project in TIA Portal, select the CPU, and verify Web server > Activate Web server is checked and the user has write rights.
- Download the hardware and software to the CPU; ensure the user-defined page is in the load memory.
- Open a browser and load
http://<CPU-IP>/awp/DBW.xml. Confirm a valid XML payload is returned. - Open a watch table in TIA Portal with the tag
"DBW".SpeedSetpoint. - Submit the HTML form
POSTshown earlier with a known value, for example 1450. - Refresh the watch table. The value must equal 1450. If unchanged, capture the HTTP transaction in Wireshark and verify the request method, URL, and body.
- Replace the form with the corrected JavaScript POST and re-run the verification.
- Confirm the diagnostic buffer contains no security rejections.
FAQ
Why can I read the S7-1200 via XML but not write to it?
Reads use HTTP GET, which the AWP handler serves from the process image. Writes require HTTP POST with a URL-encoded body matching the symbolic tag name. A GET with a body, or a local DOM mutation of a fetched XML document, never reaches the CPU's write path.
Do I need to disable optimized block access to use AWP writes?
Yes. Symbolic AWP references such as `"DBW".SpeedSetpoint` require standard (non-optimized) block access so the absolute offset can be resolved at runtime. Optimized blocks hide the offset from the Web server, and write attempts return silently.
What HTTP status code indicates a successful AWP write?
A successful POST returns HTTP 302 Found with a Location header pointing to the read URL without parameters. The client must follow the redirect (or issue a follow-up GET) to confirm the new value; a 200 OK on the POST itself indicates the redirect was followed server-side.
Can I batch multiple tag writes in a single HTTP request?
Yes. The CPU accepts up to 32 variables per POST. Concatenate key=value pairs with '&' as the separator, URL-encoding each symbolic name. For larger payloads, fragment the writes across multiple requests or use S7 communication instead of AWP.
Does AWP support HTTPS and Basic authentication?
The S7-1200 supports HTTPS when configured in CPU properties under Web server. Authentication uses a server-side login form that sets a session cookie; Basic Auth headers are accepted only on the standard pages, not on user-defined AWP fragments. Send the session cookie back on each write POST.
Why does my browser console show CORS errors when writing from a remote page?
The S7-1200 Web server does not emit CORS headers on user-defined fragments. Host the HTML page on the CPU itself, or place a reverse proxy in front of the CPU that adds the necessary Access-Control-Allow-Origin and Access-Control-Allow-Credentials headers.
Is AWP suitable for production control loops?
No. AWP write latency varies between 50 ms and several seconds under load and is not deterministic. Use PROFINET, Modbus/TCP, or S7 communication for control loops, and reserve AWP for engineering access, HMI panels, and slow supervisory setpoint changes.