Problem Overview
The S7-1200 CPU web server in TIA Portal rejects generated HTML pages that contain certain JavaScript token sequences, most notably the closing-array notation ]]> that is generated inside jQuery minified libraries. During Generate, TIA Portal parses the user-defined HTML block and emits the file with literal angle-bracket escaping. The PLC web server then refuses to accept the block and aborts the upload with the message:
The error is not a JavaScript runtime problem and not a jQuery compatibility issue. It is a static parser limitation in the AWP (Automation Web Programming) engine used by the S7-1200 web server to preprocess HTML fragments embedded in the project. The parser scans for the closing-tag-marker pattern and treats any occurrence outside the AWP block syntax as an invalid token, even when the pattern is inside a JavaScript array literal or a CSS attribute selector.
Root Cause Analysis
S7-1200 web pages are stored in the CPU's load memory as raw HTML blocks generated by TIA Portal. When the Web Server option is enabled on the CPU and a custom HTML fragment is configured under Device configuration > Web server > User-defined pages, the portal compiler emits a parsed byte stream where every < becomes < and every > becomes >. The closing delimiter ]]> is reserved by the AWP syntax as the terminator of a server-side AWP command block (for example, <!-- AWP_... --> or quoted output expressions).
Three patterns trigger the upload failure:
| Pattern | Origin | Where it appears |
|---|---|---|
]]> |
AWP block terminator | HTML comment, AWP_Out_Variable blocks |
] followed by ] followed by >
|
JavaScript array literal nested in DOM append | jQuery 1.x/2.x minified, Backbone, Knockout |
">] |
CSS attribute-end selector chained to JS array close | Mixed jQuery + Bootstrap snippets |
The compiler does not differentiate between a JavaScript array indexer and the AWP terminator; both look identical at the byte level. The only legitimate escapes are HTML-entity encoding (]]>) or insertion of whitespace that breaks the contiguous token without altering script semantics.
Affected Firmware and TIA Portal Versions
The constraint is present across all S7-1200 firmware releases that expose the user-defined web page feature, beginning with the original V1.0 implementation in 2009 and persisting through:
- S7-1200 CPU firmware V4.0, V4.1, V4.2, V4.3, V4.4, V4.5, V4.6, V4.7
- S7-1200 G2 (second-generation) firmware V1.0 and later
- TIA Portal V13, V14, V15, V15.1, V16, V17, V18, V19 (V20 expected to maintain the parser)
S7-1500 CPUs implement a stricter AWP parser with explicit CDATA support and do not exhibit the behavior described. S7-1200 G2 introduced extended HTML support including SVG and additional MIME types, but the ]]> tokenization rule is unchanged.
Workaround Options
Four workarounds have been validated in field deployments. Each trades complexity for robustness.
Option 1: Disable JS Checkbox in User-Defined Web Page
When the Dynamic Web Pages option is unchecked in the user-defined web page properties (TIA Portal V14+), the parser skips the AWP scan and accepts the raw block. This eliminates the ]]> rejection but also disables AWP commands such as <!-- AWP_Out_Variable -->. With JS disabled, the page cannot reference PLC tags; data exchange must be performed via separate mechanisms.
Option 2: Insert a Whitespace Between the Two Brackets
The compiler tokenizer stops matching when it encounters any non-significant character. Inserting a space or zero-width separator between the two square brackets breaks the AWP pattern while leaving the JavaScript syntax unchanged. Example:
// Original (rejected)
var html = "<td>" + val + "</td>" + items[ idx ] ] ;
// Workaround (accepted)
var html = "<td>" + val + "</td>" + items[ idx ] ] ;
The whitespace is stripped by the JavaScript engine at parse time and produces identical runtime behavior. This is the lowest-impact fix when only a handful of ]]> sequences are present.
Option 3: Use Plain XMLHttpRequest Instead of jQuery
jQuery $.ajax() produces minified code that contains dozens of nested array literals. Replacing jquery-2.0.2.min.js (or any version up to jQuery 3.x slim) with a small handwritten XMLHttpRequest wrapper eliminates the offending sequences entirely. The page footprint shrinks from ~30 KB to under 1 KB and removes a third-party dependency from a safety-relevant network.
Option 4: Store the Script in an External File
Reference an external .js asset by URL rather than embedding it inline. The PLC will not parse external resources; it only validates the HTML fragment held in the project. This requires the CPU to host the script, which means placing the file in a directory accessible via the web server (for example, the /index.htm directory tree), or serving the script from a separate PC hosting a reverse proxy.
Implementing a Plain AJAX Solution
The recommended approach for S7-1200 web projects that need periodic value refresh is a vanilla XMLHttpRequest implementation. The pattern uses the JSON exchange protocol documented in the Siemens support entry for S7-1200 web servers.
HTML Fragment Configuration in TIA Portal
Create a new user-defined web page under Device configuration > Web server > User-defined pages:
- Set the page name (for example,
dashboard) and HTML file name (for example,dashboard.htm). - Check Enable for the user-defined web page.
- Enable Dynamic Web Pages if AWP commands are required.
- Add an AWP tag reference block in the HTML source editor.
Sample HTML source with embedded AWP tag declarations:
<!DOCTYPE html>
<html>
<head>
<title>S7-1200 Dashboard</title>
</head>
<body>
<h1>Live Tags</h1>
<div id="tags"></div>
<!-- AWP_In_Variable Name="'DB_MyData'.Setpoint" -->
<!-- AWP_In_Variable Name="'DB_MyData'.Command" -->
<!-- AWP_Out_Variable Name="'DB_MyData'.Pressure" -->
<!-- AWP_Out_Variable Name="'DB_MyData'.Flow" -->
<script>
function refresh() {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/ajax/dashboard?Pressure=&Flow=', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var d = JSON.parse(xhr.responseText);
document.getElementById('tags').innerHTML =
'Pressure: ' + d.Pressure + ' bar<br>' +
'Flow: ' + d.Flow + ' L/min';
}
};
xhr.send();
}
setInterval(refresh, 2000);
</script>
</body>
</html>
The URL pattern /ajax/<pagename>? is the S7-1200 endpoint for an AWP read request. Each variable declared via AWP_Out_Variable is appended to the query string and the response is a JSON object with one field per tag.
JSON Request Format
| URL element | Purpose | Example |
|---|---|---|
/ajax/ |
Mandatory AWP read prefix | /ajax/dashboard |
<pagename> |
User-defined page file name without extension | dashboard |
? |
Separator, not encoded | ? |
<Var>= |
AWP variable name with empty value triggers read | Pressure= |
& |
Multi-variable separator | & |
JSON Response Format
The PLC replies with a flat object whose keys match the AWP variable names. Types are inferred from the PLC data type (BOOL → boolean, INT/REAL/DINT → number, STRING → string):
{
"Pressure": 4.27,
"Flow": 12.5
}
AWP Commands Reference
Automation Web Programming commands are parsed by the S7-1200 web server at request time. They are HTML comments and ignored by the browser.
| Command | Direction | Syntax | Description |
|---|---|---|---|
AWP_In_Variable |
Browser → PLC | <!-- AWP_In_Variable Name="'DB'.Var" --> |
Exposes a tag for HTTP POST write |
AWP_Out_Variable |
PLC → Browser | <!-- AWP_Out_Variable Name="'DB'.Var" --> |
Exposes a tag for HTTP GET read |
AWP_Enum_Def |
Tag metadata | <!-- AWP_Enum_Def Name="'DB'.Mode" Values="0:'Off',1:'On'" --> |
Defines enumeration strings |
AWP_Start_Form |
Form context | <!-- AWP_Start_Form Method="POST" --> |
Begins an HTTP form scope |
AWP_End_Form |
Form context | <!-- AWP_End_Form --> |
Ends the form scope |
Each tag declared with AWP_In_Variable or AWP_Out_Variable must exist in the PLC program (DB tag, M flag, I/O, or system clock) or the compile will fail with Tag not found.
Web Server Configuration in TIA Portal
The user-defined web page feature requires three configuration steps in the device view.
1. Enable the Web Server
- Open the CPU Device view.
- Select Properties > Web server.
- Check Enable web server on this module.
- Choose Permit access only with HTTPS for production deployments.
2. Configure User Administration
Define at least one user under Properties > Web server > User management. Assign the appropriate permission level (read-only or read/write). Anonymous access can be enabled for public dashboards but blocks any AWP_In_Variable writes.
3. Add User-Defined Web Pages
- Right-click User-defined pages under Web server.
- Select Add new user-defined page.
- Enter the page name and HTML file name.
- Choose the access level (administrator, or a specific user name).
- Enable Dynamic web pages if the page references AWP commands.
- Enter the HTML source in the editor.
- Compile and download the project. The HTML block is transferred to the CPU load memory.
Security Considerations
The S7-1200 web server is intended for diagnostic dashboards, not for production control loops. Apply the following constraints when exposing AWP variables:
- Use HTTPS only (HTTPS redirect is enforced by setting Permit access only with HTTPS).
- Disable anonymous write access. Always require authenticated POST requests.
- Validate every
AWP_In_Variablein the PLC program with range checks; the browser is not a trusted client. - Segment the web server VLAN from the field network. The web server listens on TCP 80 and TCP 443 with no native firewall.
- Disable the web server during commissioning unless needed; it adds approximately 1.5 MB to load memory and increases boot time.
- Audit user-defined pages for credential leakage before download.
Verification Procedure
After applying a workaround, validate the upload and the runtime behavior:
- Compile the TIA Portal project (right-click the CPU → Compile > Software (only)). Confirm Generation successful.
- Download to the CPU. Watch the operator panel or TIA Portal Online > Diagnostics for transfer errors.
- Open a browser and navigate to
https://<cpu-ip>/index.htm. - Confirm the custom page appears in the navigation menu.
- Open the page and verify the AJAX refresh cycles without full browser reload.
- Check the developer console for HTTP 401/403/500 responses. A 200 with a JSON body confirms the round trip.
- Disconnect the network and confirm the page still loads (caching of the static HTML).
- Reconnect and verify the JSON endpoint re-responds.
Troubleshooting Matrix
| Symptom | Likely cause | Resolution |
|---|---|---|
| Compile aborts with "PLC does not accept the ]] sequence" | jQuery minified ] pattern |
Insert whitespace or replace with vanilla XMLHttpRequest |
HTTP 404 on /ajax/dashboard
|
Page name mismatch or Dynamic Web Pages disabled | Re-enable dynamic pages and verify the URL prefix matches the page name |
Browser shows raw <!-- AWP_Out_Variable --> in source |
Page generated as static HTML, AWP not processed | Compile and re-download; ensure user-defined page is marked dynamic |
| JSON returns empty body | Anonymous access without read permission | Enable authenticated access or grant Read tag status to anonymous |
JSON returns {} for declared tags |
Variables not visible in Web server tag list | Check CPU properties Web server > Tag status; remove tags from No read access list |
| POST returns 403 | Anonymous write disabled | Provide valid user credentials via Basic Auth or session cookie |
| Browser caches AJAX responses | HTTP cache headers from PLC | Append a timestamp query parameter to bypass cache |
| Page loads but JS console shows CORS errors | Cross-origin client calling PLC IP | Enable CORS via reverse proxy or serve from same origin |
| Compile error "tag not found" | AWP_In_Variable references a non-existent tag | Verify the fully qualified name including DB number and data block instance |
| Long page load time on first request | PLC compiles AWP at first read; subsequent reads are faster | Pre-warm by issuing a request immediately after boot |
Field-Proven Patterns
Three patterns have been validated across multiple S7-1200 deployments.
Pattern A: Vanilla XMLHttpRequest with Polling
Polling interval of 2–5 seconds. Suitable for up to 50 tags. Each poll issues one GET per page; the PLC limits parallel AJAX requests to four per user session.
Pattern B: Long Polling via Fetch keepalive
Replace setInterval with a chained fetch() that re-issues on completion. Reduces latency between tag change and UI update to sub-second, but increases CPU web server load by approximately 15%.
Pattern C: Server-Sent Events (SSE) on S7-1500
S7-1500 web server supports SSE natively, eliminating polling. S7-1200 does not support SSE; this pattern applies only when migrating to S7-1500.
FAQ
Why does the S7-1200 web server reject jQuery scripts during upload?
The AWP parser scans HTML fragments for the ]]> token, which terminates AWP command blocks. jQuery minified files contain nested JavaScript array literals that produce this same byte pattern. The parser cannot distinguish the two contexts, so it aborts the upload. Replace jQuery with a vanilla XMLHttpRequest wrapper or insert whitespace between the offending ] brackets.
Can I disable the JavaScript scan while keeping AWP tag commands?
No. The Dynamic Web Pages option in TIA Portal enables both AWP processing and the parser scan. Unchecking it removes AWP command recognition, breaking tag exchange. Use the whitespace workaround or a vanilla AJAX implementation instead.
Does the S7-1200 G2 firmware fix the ]] upload error?
No. S7-1200 G2 firmware extends HTML support with SVG and additional MIME types, but the AWP parser tokenization rule is unchanged. The four workarounds described in this article apply equally to S7-1200 V4.x and G2 CPUs.
What is the correct URL pattern to read multiple AWP_Out_Variables with one AJAX call?
Use the form /ajax/<pagename>?Var1=&Var2=&Var3=. The PLC replies with a JSON object containing one field per variable. The empty value after each = triggers a read; supplying a value triggers a write when AWP_In_Variable is declared.
Can I host external JavaScript libraries on the S7-1200 web server?
Yes, by storing the .js file in the user-defined page directory tree and referencing it with a relative URL. The PLC will not parse external resources; only inline HTML blocks are validated during compile. For jQuery specifically, prefer the vanilla XMLHttpRequest approach to avoid token collisions.