diff --git a/src/org/openstreetmap/josm/data/validation/OsmValidator.java b/src/org/openstreetmap/josm/data/validation/OsmValidator.java
index 1a9896c..acd2efa 100644
--- a/src/org/openstreetmap/josm/data/validation/OsmValidator.java
+++ b/src/org/openstreetmap/josm/data/validation/OsmValidator.java
@@ -41,6 +41,7 @@ import org.openstreetmap.josm.data.validation.tests.InternetTags;
 import org.openstreetmap.josm.data.validation.tests.Lanes;
 import org.openstreetmap.josm.data.validation.tests.LongSegment;
 import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
+import org.openstreetmap.josm.data.validation.tests.Maxspeed;
 import org.openstreetmap.josm.data.validation.tests.MultipolygonTest;
 import org.openstreetmap.josm.data.validation.tests.NameMismatch;
 import org.openstreetmap.josm.data.validation.tests.OpeningHourTest;
@@ -126,6 +127,7 @@ public class OsmValidator implements LayerChangeListener {
         InternetTags.class, // 3300 .. 3399
         ApiCapabilitiesTest.class, // 3400 .. 3499
         LongSegment.class, // 3500 .. 3599
+        Maxspeed.class, // 3600 .. 3699
     };
 
     private static Map<String, Test> allTestsMap;
diff --git a/src/org/openstreetmap/josm/data/validation/tests/Maxspeed.java b/src/org/openstreetmap/josm/data/validation/tests/Maxspeed.java
new file mode 100644
index 0000000..4c49f10
--- /dev/null
+++ b/src/org/openstreetmap/josm/data/validation/tests/Maxspeed.java
@@ -0,0 +1,78 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.validation.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.regex.Pattern;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.validation.Severity;
+import org.openstreetmap.josm.data.validation.Test.TagTest;
+import org.openstreetmap.josm.data.validation.TestError;
+import org.openstreetmap.josm.tools.Predicates;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * Test that validates {@code maxspeed:} tags.
+ *
+ */
+public class Maxspeed extends TagTest {
+    protected static Pattern maxspeedPattern = Pattern.compile("^((\\+?\\d*\\.?\\d*)(\\s(mph|knots|kmh))?|signals|none|walk)$");
+
+    protected static Pattern sourceOrZoneMaxspeedPattern = Pattern.compile("^([A-Z]{2}):([0-9a-zA-Z_-]+)(:[0-9a-zA-Z_-]+)+$");
+
+    private static final String[] BLACKLIST = {
+    };
+
+    /**
+     * Constructs a new {@code Maxspeed} test.
+     */
+    public Maxspeed() {
+        super(tr("Maxspeed"),
+              tr("This tests for maxspeeds, which are usually errors."));
+    }
+
+    protected void checkMaxspeedByKey(final OsmPrimitive p, String maxspeedKey, Pattern pattern, int errorCode, String errorMessage) {
+        final Collection<String> keysForPattern = new ArrayList<>(Utils.filter(p.keySet(),
+                Predicates.stringContainsPattern(Pattern.compile(maxspeedKey))));
+        keysForPattern.removeAll(Arrays.asList(BLACKLIST));
+        if (keysForPattern.isEmpty()) {
+            // nothing to check
+            return;
+        }
+        String value = p.get(keysForPattern.iterator().next());
+        if( !pattern.matcher(value).find()) {
+            errors.add(new TestError(this, Severity.WARNING, tr(errorMessage, maxspeedKey), errorCode, p));
+        }
+    }
+
+    protected void checkMaxspeed(final OsmPrimitive p) {
+        final Float backward = Float.parseFloat(Utils.firstNonNull(p.get("maxspeed:backward"), 0.0).toString());
+        final Float forward = Float.parseFloat(Utils.firstNonNull(p.get("maxspeed:forward"), 0.0).toString());
+        try {
+        if (Float.compare(backward, forward) == 0 && backward != 0) {
+            System.out.println("backward:" + backward);
+            System.out.println("forward:" + forward);
+            errors.add(new TestError(this, Severity.WARNING,
+                    tr("Value of {0} and {1} are equals. You should merge them in one tag : {2}", "maxspeed:backward", "maxspeed:forward", "maxspeed"), 3610, p));
+            }
+        } catch (NumberFormatException ignore) {
+            Main.debug(ignore.getMessage());
+        }
+    }
+
+    @Override
+    public void check(OsmPrimitive p) {
+        checkMaxspeedByKey(p, "maxspeed", maxspeedPattern, 3601, "Value of {0} is not correct");
+        checkMaxspeedByKey(p, "maxspeed:backward", maxspeedPattern, 3601, "Value of {0} is not correct");
+        checkMaxspeedByKey(p, "maxspeed:forward", maxspeedPattern, 3601, "Value of {0} is not correct");
+        checkMaxspeedByKey(p, "source:maxspeed", sourceOrZoneMaxspeedPattern, 3602, "Value of {0} is not correct");
+        checkMaxspeedByKey(p, "zone:maxspeed", sourceOrZoneMaxspeedPattern, 3603, "Value of {0} is not correct");
+        checkMaxspeed(p);
+    }
+
+}
diff --git a/test/unit/org/openstreetmap/josm/data/validation/tests/MaxspeedTest.groovy b/test/unit/org/openstreetmap/josm/data/validation/tests/MaxspeedTest.groovy
new file mode 100644
index 0000000..1ab3b72
--- /dev/null
+++ b/test/unit/org/openstreetmap/josm/data/validation/tests/MaxspeedTest.groovy
@@ -0,0 +1,61 @@
+package org.openstreetmap.josm.data.validation.tests
+
+import org.openstreetmap.josm.JOSMFixture
+import org.openstreetmap.josm.data.osm.OsmUtils
+
+class MaxspeedTest extends GroovyTestCase {
+
+    Maxspeed lanes = new Maxspeed()
+
+    @Override
+    void setUp() {
+        JOSMFixture.createUnitTestFixture().init()
+        lanes.initialize()
+        lanes.startTest(null)
+    }
+
+    void test1() {
+        lanes.check(OsmUtils.createPrimitive("way maxspeed=30z"))
+        assert lanes.errors.get(0).getMessage() == "Value of maxspeed is not correct"
+    }
+
+    void test2() {
+        lanes.check(OsmUtils.createPrimitive("way maxspeed=40knot"))
+        assert lanes.errors.get(0).getMessage() == "Value of maxspeed is not correct"
+    }
+
+    void test3() {
+        lanes.check(OsmUtils.createPrimitive("way maxspeed=50mph"))
+        assert lanes.errors.get(0).getMessage() == "Value of maxspeed is not correct"
+    }
+
+    void test4() {
+        lanes.check(OsmUtils.createPrimitive("way maxspeed=50"))
+        assert lanes.errors.size() == 0
+    }
+
+    void test5() {
+        lanes.check(OsmUtils.createPrimitive("way maxspeed=50 mph"))
+        assert lanes.errors.size() == 0
+    }
+
+    void test6() {
+        lanes.check(OsmUtils.createPrimitive("way maxspeed:forward=50 maxspeed:backward=50"))
+        assert lanes.errors.get(0).getMessage() == "Value of maxspeed:backward and maxspeed:forward are equals. You should merge them in one tag : maxspeed"
+    }
+
+    void test7() {
+        lanes.check(OsmUtils.createPrimitive("way maxspeed=50 source:maxspeed=50"))
+        assert lanes.errors.get(0).getMessage() == "Value of source:maxspeed is not correct"
+    }
+
+    void test8() {
+        lanes.check(OsmUtils.createPrimitive("way maxspeed=50 source:maxspeed=DE:"))
+        assert lanes.errors.get(0).getMessage() == "Value of source:maxspeed is not correct"
+    }
+
+    void test9() {
+        lanes.check(OsmUtils.createPrimitive("way maxspeed=50 source:maxspeed=DE:fr:"))
+        assert lanes.errors.get(0).getMessage() == "Value of source:maxspeed is not correct"
+    }
+}
